@rness/cli 0.18.0 → 0.18.1
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 +24 -1
- package/dist/bin/rness.js +1 -1
- package/dist/{chunk-VGBS7HDY.js → chunk-JHU2G7K7.js} +109 -67
- package/dist/chunk-JHU2G7K7.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/scaffold/CONVENTIONS.md +5 -0
- package/dist/chunk-VGBS7HDY.js.map +0 -1
package/README.md
CHANGED
|
@@ -133,7 +133,8 @@ the files a given agent needs, for the agents the team declares in
|
|
|
133
133
|
by `rness doc new <collection>` — the second pre-approved line of each
|
|
134
134
|
skill, so that every runtime and a bare terminal get the same number;
|
|
135
135
|
`rness validate` refuses two documents with one number, the case a merge
|
|
136
|
-
of two branches can still produce
|
|
136
|
+
of two branches can still produce, and a document named without one
|
|
137
|
+
(`NNNN-<slug>.md`, never a date). Each skill records its session: it adds
|
|
137
138
|
`{ id: <session id>, agent: <model> }` to `sessions:` in the front matter
|
|
138
139
|
of what it writes or closes. The agent is the model it runs as, as Claude
|
|
139
140
|
Code names it (`Claude Opus 5.5`): no variable gives it to a skill. An
|
|
@@ -603,6 +604,28 @@ per repository, the files to commit there.
|
|
|
603
604
|
Exit codes: 0 success, 1 failure, 2 usage — or a refusal without a TTY.
|
|
604
605
|
`RNESS_DEBUG=1` adds stack traces; `RNESS_NO_DELEGATE=1` skips the delegation.
|
|
605
606
|
|
|
607
|
+
## 0.18.1 — a document without a number is caught; small fixes
|
|
608
|
+
|
|
609
|
+
- `rness validate` and the edit hook name a document of `adr/`, `specs/` or
|
|
610
|
+
`plans/` whose file name has no number, with the next one:
|
|
611
|
+
`plans/2026-10-02-x.md: not numbered; name it 0002-<slug>.md, the next
|
|
612
|
+
number of plans`. A plan another agent's skill wrote with a dated name
|
|
613
|
+
is the usual case; rename it, or let the agent do it when the hook
|
|
614
|
+
tells it. A workspace with such a file fails `validate` (and its CI)
|
|
615
|
+
after the upgrade until it is renamed. The scaffold's `CONVENTIONS.md`
|
|
616
|
+
states the rule, and `upgrade` merges it in.
|
|
617
|
+
- A dated file name is no longer read as a number: `rness doc new` after
|
|
618
|
+
`2026-10-02-x.md` gave `2027`, and two dated plans of one year were
|
|
619
|
+
reported as one number twice.
|
|
620
|
+
- `rness doc new` racing another one at the same number says `<file>
|
|
621
|
+
exists; nothing written` (exit 1) instead of a raw `EEXIST`.
|
|
622
|
+
- `1 repository`, not `1 repositories`, in `create` and `sync`.
|
|
623
|
+
- When the SSH test passes only by asking for the key's passphrase,
|
|
624
|
+
`create`, `add` and `sync` say once how to load it into an agent
|
|
625
|
+
(`ssh-add`), since every clone would ask again.
|
|
626
|
+
- Ctrl+C while `create` waits for an organization's approval is a cancel
|
|
627
|
+
in the plain look too: `cancelled`, nothing written, exit 0 (it was 130).
|
|
628
|
+
|
|
606
629
|
## 0.18.0 — the wizard asks where first and waits for the approval; the skills on one grammar; Node 22.17
|
|
607
630
|
|
|
608
631
|
- The published CLI runs on **Node 22.17 or later** (ADR 0010); the guard
|
package/dist/bin/rness.js
CHANGED
|
@@ -508,7 +508,7 @@ function parseRepoSpec(spec, org, host) {
|
|
|
508
508
|
}
|
|
509
509
|
|
|
510
510
|
// src/version.ts
|
|
511
|
-
var VERSION = "0.18.
|
|
511
|
+
var VERSION = "0.18.1";
|
|
512
512
|
|
|
513
513
|
// src/core/pm.ts
|
|
514
514
|
import { execFile as execFile2 } from "child_process";
|
|
@@ -744,7 +744,13 @@ import { join as join5 } from "path";
|
|
|
744
744
|
|
|
745
745
|
// src/core/documents.ts
|
|
746
746
|
import { posix as posix2 } from "path";
|
|
747
|
+
function documentDate(rel) {
|
|
748
|
+
return /^((?:19|20)\d{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]))-/.exec(
|
|
749
|
+
posix2.basename(rel)
|
|
750
|
+
)?.[1] ?? null;
|
|
751
|
+
}
|
|
747
752
|
function documentNumber(rel) {
|
|
753
|
+
if (documentDate(rel) !== null) return null;
|
|
748
754
|
return /^(\d{4})-/.exec(posix2.basename(rel))?.[1] ?? null;
|
|
749
755
|
}
|
|
750
756
|
function documentTitle(heading, number) {
|
|
@@ -788,9 +794,16 @@ async function checkContract(rnessDir) {
|
|
|
788
794
|
`${where}: the number ${number} is also that of ${files.filter((f) => f !== where).join(", ")}`
|
|
789
795
|
);
|
|
790
796
|
}
|
|
797
|
+
const next = String(
|
|
798
|
+
Math.max(0, ...[...byNumber.keys()].map(Number)) + 1
|
|
799
|
+
).padStart(4, "0");
|
|
791
800
|
for (const item of items) {
|
|
792
801
|
const where = `${name}/${item.rel}`;
|
|
793
802
|
if (SKIP.has(where)) continue;
|
|
803
|
+
if (allowed !== void 0 && documentNumber(item.rel) === null)
|
|
804
|
+
problems.push(
|
|
805
|
+
`${where}: not numbered; name it ${next}-<slug>.md, the next number of ${name}`
|
|
806
|
+
);
|
|
794
807
|
if (item.fieldsError !== null) {
|
|
795
808
|
problems.push(`${where}: invalid front matter (${item.fieldsError})`);
|
|
796
809
|
continue;
|
|
@@ -1127,8 +1140,8 @@ function sessionsOf(item) {
|
|
|
1127
1140
|
}
|
|
1128
1141
|
function rowOf(dir, item) {
|
|
1129
1142
|
const base = posix3.basename(item.rel, ".md");
|
|
1130
|
-
const date =
|
|
1131
|
-
const number =
|
|
1143
|
+
const date = documentDate(item.rel);
|
|
1144
|
+
const number = documentNumber(item.rel);
|
|
1132
1145
|
const sessions = sessionsOf(item);
|
|
1133
1146
|
return {
|
|
1134
1147
|
id: date ?? number ?? base,
|
|
@@ -3948,6 +3961,9 @@ function inverse(text, stream = process.stdout) {
|
|
|
3948
3961
|
const painted = styleText("inverse", text, { stream, validateStream: true });
|
|
3949
3962
|
return painted === text ? plain : painted;
|
|
3950
3963
|
}
|
|
3964
|
+
function count(n, [one, many]) {
|
|
3965
|
+
return `${n} ${n === 1 ? one : many}`;
|
|
3966
|
+
}
|
|
3951
3967
|
function grey(text, stream = process.stdout) {
|
|
3952
3968
|
if (noColor()) return text;
|
|
3953
3969
|
return styleText("gray", text, { stream, validateStream: true });
|
|
@@ -4021,12 +4037,12 @@ async function detectGithubSsh(opts) {
|
|
|
4021
4037
|
child.stderr?.setEncoding("utf8");
|
|
4022
4038
|
child.stderr?.on("data", (chunk) => chunks.push(chunk));
|
|
4023
4039
|
let settled = false;
|
|
4024
|
-
const settle = (
|
|
4040
|
+
const settle = (access2) => {
|
|
4025
4041
|
if (settled) return;
|
|
4026
4042
|
settled = true;
|
|
4027
4043
|
clearTimeout(timer);
|
|
4028
4044
|
child.stderr?.destroy();
|
|
4029
|
-
resolve8(
|
|
4045
|
+
resolve8(access2);
|
|
4030
4046
|
};
|
|
4031
4047
|
const timer = setTimeout(() => {
|
|
4032
4048
|
child.kill("SIGKILL");
|
|
@@ -4071,8 +4087,8 @@ async function testGithubSsh(transport, interactive, announce) {
|
|
|
4071
4087
|
};
|
|
4072
4088
|
}
|
|
4073
4089
|
var loggedInAs = (login) => login === void 0 || login === null ? "" : `, logged in as ${login}`;
|
|
4074
|
-
function usingSentence(
|
|
4075
|
-
return
|
|
4090
|
+
function usingSentence(access2, login) {
|
|
4091
|
+
return access2.ok ? `SSH works for github.com (as ${access2.login}) \u2014 repositories are cloned over SSH` : `No SSH access to github.com (${access2.reason}) \u2014 repositories are cloned over HTTPS${loggedInAs(login)}`;
|
|
4076
4092
|
}
|
|
4077
4093
|
function isSshUrl(url, hosts) {
|
|
4078
4094
|
return url.startsWith(hosts.ssh);
|
|
@@ -4083,8 +4099,8 @@ var CHECKING = [
|
|
|
4083
4099
|
"ssh access to github.com\u2026",
|
|
4084
4100
|
"Checking SSH access to github.com \u2014 ssh may ask for your passphrase"
|
|
4085
4101
|
];
|
|
4086
|
-
function usingRest(
|
|
4087
|
-
return
|
|
4102
|
+
function usingRest(access2, login) {
|
|
4103
|
+
return access2.ok ? `ssh (github.com as ${access2.login})` : `https (ssh to github.com unavailable: ${access2.reason}${loggedInAs(login)})`;
|
|
4088
4104
|
}
|
|
4089
4105
|
function agentHintLines(platform = process.platform) {
|
|
4090
4106
|
const mac = platform === "darwin";
|
|
@@ -4094,6 +4110,12 @@ function agentHintLines(platform = process.platform) {
|
|
|
4094
4110
|
` ~/.ssh/config: AddKeysToAgent yes${mac ? ", UseKeychain yes" : ""}`
|
|
4095
4111
|
];
|
|
4096
4112
|
}
|
|
4113
|
+
function passphraseHint(platform = process.platform) {
|
|
4114
|
+
return [
|
|
4115
|
+
"ssh asked for your passphrase, and asks again for every repository unless an agent holds the key:",
|
|
4116
|
+
...agentHintLines(platform).slice(1)
|
|
4117
|
+
].join("\n");
|
|
4118
|
+
}
|
|
4097
4119
|
function sshWorkspaceLines(reason3) {
|
|
4098
4120
|
return [
|
|
4099
4121
|
`this workspace clones over SSH (rness.json) but ssh to github.com fails: ${reason3}`,
|
|
@@ -5509,7 +5531,7 @@ async function askWhatToSync(input) {
|
|
|
5509
5531
|
{
|
|
5510
5532
|
value: SELECT_ALL,
|
|
5511
5533
|
label: "Select all",
|
|
5512
|
-
hint: `${input.missing.length
|
|
5534
|
+
hint: `${count(input.missing.length, ["repository", "repositories"])} from rness.json`
|
|
5513
5535
|
},
|
|
5514
5536
|
...input.missing.map((name) => ({ value: name, label: name }))
|
|
5515
5537
|
],
|
|
@@ -5610,15 +5632,16 @@ async function syncCommand(opts, deps = {}) {
|
|
|
5610
5632
|
if (!overSsh) ui.gitIsSilent = true;
|
|
5611
5633
|
if (overSsh) {
|
|
5612
5634
|
const interactive = opts.yes !== true && terminal.isTty();
|
|
5613
|
-
const { access:
|
|
5635
|
+
const { access: access2, unattended } = await testGithubSsh(
|
|
5614
5636
|
transport,
|
|
5615
5637
|
interactive,
|
|
5616
5638
|
() => ui.line(...CHECKING)
|
|
5617
5639
|
);
|
|
5618
5640
|
if (unattended) ui.gitIsSilent = true;
|
|
5619
|
-
if (
|
|
5641
|
+
else if (access2.ok) ui.hint(passphraseHint(), "stderr");
|
|
5642
|
+
if (!access2.ok) {
|
|
5620
5643
|
process.stderr.write(
|
|
5621
|
-
`${[...sshWorkspaceLines(
|
|
5644
|
+
`${[...sshWorkspaceLines(access2.reason), ...INSTEAD_OF_LINES].join("\n")}
|
|
5622
5645
|
`
|
|
5623
5646
|
);
|
|
5624
5647
|
return 1;
|
|
@@ -5847,20 +5870,21 @@ async function addCommand(spec, opts, deps = {}) {
|
|
|
5847
5870
|
if (host === void 0 && opts.https === true) host = transport.hosts.https;
|
|
5848
5871
|
if (host === void 0 && FULL_URL2.test(spec)) host = transport.hosts.https;
|
|
5849
5872
|
if (host === void 0) {
|
|
5850
|
-
const { access:
|
|
5873
|
+
const { access: access2, unattended } = await testGithubSsh(
|
|
5851
5874
|
transport,
|
|
5852
5875
|
interactive,
|
|
5853
5876
|
() => ui.line(...CHECKING)
|
|
5854
5877
|
);
|
|
5855
|
-
if (unattended || !
|
|
5878
|
+
if (unattended || !access2.ok) ui.gitIsSilent = true;
|
|
5879
|
+
else ui.hint(passphraseHint(), "stderr");
|
|
5856
5880
|
const overSsh = Object.values(manifest.repos).some(
|
|
5857
5881
|
(r) => isSshUrl(r.url, transport.hosts)
|
|
5858
5882
|
);
|
|
5859
|
-
if (
|
|
5860
|
-
ui.line("using", usingRest(
|
|
5861
|
-
host =
|
|
5883
|
+
if (access2.ok || !overSsh) {
|
|
5884
|
+
ui.line("using", usingRest(access2), usingSentence(access2));
|
|
5885
|
+
host = access2.ok ? transport.hosts.ssh : transport.hosts.https;
|
|
5862
5886
|
} else {
|
|
5863
|
-
const lines = sshWorkspaceLines(
|
|
5887
|
+
const lines = sshWorkspaceLines(access2.reason);
|
|
5864
5888
|
if (!interactive) {
|
|
5865
5889
|
process.stderr.write(
|
|
5866
5890
|
`${[...lines, httpsFlagLine(spec)].join("\n")}
|
|
@@ -6262,12 +6286,12 @@ async function checkAccess(input) {
|
|
|
6262
6286
|
const login = (await provider.identity())?.login;
|
|
6263
6287
|
const as = login === void 0 ? "" : ` (as ${login})`;
|
|
6264
6288
|
const member = () => ui.line("member", `${org}${as}`, `You are a member of ${org}${as}`);
|
|
6265
|
-
const
|
|
6266
|
-
if (
|
|
6289
|
+
const access2 = await provider.organizationAccess(org);
|
|
6290
|
+
if (access2 === "member") {
|
|
6267
6291
|
member();
|
|
6268
|
-
return
|
|
6292
|
+
return access2;
|
|
6269
6293
|
}
|
|
6270
|
-
if (
|
|
6294
|
+
if (access2 !== "restricted") return access2;
|
|
6271
6295
|
const url = provider.approvalUrl();
|
|
6272
6296
|
ui.warn(
|
|
6273
6297
|
`${org} has not approved rness, so its private repositories are hidden`
|
|
@@ -6296,23 +6320,42 @@ async function checkAccess(input) {
|
|
|
6296
6320
|
const who = input.canGrant === true ? org : `an owner of ${org}`;
|
|
6297
6321
|
const waiting = `for ${who} to approve rness on github.com`;
|
|
6298
6322
|
if (!ui.session) ui.line("waiting", `${waiting}\u2026`);
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6323
|
+
let interrupted = false;
|
|
6324
|
+
let wake = () => void 0;
|
|
6325
|
+
const interrupt = () => {
|
|
6326
|
+
interrupted = true;
|
|
6327
|
+
wake();
|
|
6328
|
+
};
|
|
6329
|
+
if (!ui.session) process.once("SIGINT", interrupt);
|
|
6330
|
+
let outcome;
|
|
6331
|
+
try {
|
|
6332
|
+
outcome = await ui.step(
|
|
6333
|
+
{
|
|
6334
|
+
doing: `waiting ${waiting}`,
|
|
6335
|
+
sentence: `Waiting ${waiting}`,
|
|
6336
|
+
quiet: true
|
|
6337
|
+
},
|
|
6338
|
+
async () => {
|
|
6339
|
+
const deadline = now() + APPROVAL_WAIT_MS;
|
|
6340
|
+
for (; ; ) {
|
|
6341
|
+
await Promise.race([
|
|
6342
|
+
sleep(APPROVAL_INTERVAL_MS),
|
|
6343
|
+
new Promise((resolve8) => {
|
|
6344
|
+
wake = resolve8;
|
|
6345
|
+
})
|
|
6346
|
+
]);
|
|
6347
|
+
if (interrupted) return "cancelled";
|
|
6348
|
+
const again = await provider.organizationAccess(org);
|
|
6349
|
+
if (again === "member" || again === "not-member") return again;
|
|
6350
|
+
if (now() >= deadline) return "timeout";
|
|
6351
|
+
}
|
|
6352
|
+
},
|
|
6353
|
+
(r) => r === "member" ? ["member", `${org}${as}`] : null
|
|
6354
|
+
);
|
|
6355
|
+
} finally {
|
|
6356
|
+
process.off("SIGINT", interrupt);
|
|
6357
|
+
}
|
|
6358
|
+
if (outcome === "cancelled") return outcome;
|
|
6316
6359
|
if (outcome === "member") {
|
|
6317
6360
|
if (!ui.session) member();
|
|
6318
6361
|
return outcome;
|
|
@@ -6804,7 +6847,7 @@ async function pickRepositories(input) {
|
|
|
6804
6847
|
// list below it must agree (spec 0009).
|
|
6805
6848
|
(l) => [
|
|
6806
6849
|
"listed",
|
|
6807
|
-
`${l.repositories.filter(isMemberRepo).length
|
|
6850
|
+
`${count(l.repositories.filter(isMemberRepo).length, ["repository", "repositories"])} of ${org}`
|
|
6808
6851
|
]
|
|
6809
6852
|
);
|
|
6810
6853
|
listed = listing.repositories;
|
|
@@ -7085,6 +7128,7 @@ async function createCommand(opts, deps = {}) {
|
|
|
7085
7128
|
() => ui.line(...CHECKING)
|
|
7086
7129
|
);
|
|
7087
7130
|
if (sshTest.unattended) ui.gitIsSilent = true;
|
|
7131
|
+
else if (sshTest.access.ok) ui.hint(passphraseHint(), "stderr");
|
|
7088
7132
|
}
|
|
7089
7133
|
return sshTest.access;
|
|
7090
7134
|
};
|
|
@@ -7094,12 +7138,12 @@ async function createCommand(opts, deps = {}) {
|
|
|
7094
7138
|
if (opts.host !== void 0) return opts.host;
|
|
7095
7139
|
if (opts.ssh === true) return transport.hosts.ssh;
|
|
7096
7140
|
if (opts.https === true) return transport.hosts.https;
|
|
7097
|
-
const
|
|
7098
|
-
const github =
|
|
7141
|
+
const access2 = await testSsh();
|
|
7142
|
+
const github = access2.ok ? null : await getProvider();
|
|
7099
7143
|
const login = github?.authenticated ? (await github.identity())?.login : void 0;
|
|
7100
|
-
ui.line("using", usingRest(
|
|
7101
|
-
if (!
|
|
7102
|
-
return
|
|
7144
|
+
ui.line("using", usingRest(access2, login), usingSentence(access2, login));
|
|
7145
|
+
if (!access2.ok) ui.gitIsSilent = true;
|
|
7146
|
+
return access2.ok ? transport.hosts.ssh : transport.hosts.https;
|
|
7103
7147
|
};
|
|
7104
7148
|
let loaded;
|
|
7105
7149
|
const prompts = async () => loaded ??= await terminal.prompts();
|
|
@@ -7273,10 +7317,10 @@ async function createCommand(opts, deps = {}) {
|
|
|
7273
7317
|
(r) => isSshUrl(r.url, transport.hosts)
|
|
7274
7318
|
);
|
|
7275
7319
|
if (overSsh && opts.ssh !== true && opts.host === void 0) {
|
|
7276
|
-
const
|
|
7277
|
-
if (!
|
|
7320
|
+
const access2 = await testSsh();
|
|
7321
|
+
if (!access2.ok) {
|
|
7278
7322
|
process.stderr.write(
|
|
7279
|
-
`${[...sshWorkspaceLines(
|
|
7323
|
+
`${[...sshWorkspaceLines(access2.reason), ...INSTEAD_OF_LINES].join("\n")}
|
|
7280
7324
|
`
|
|
7281
7325
|
);
|
|
7282
7326
|
return 1;
|
|
@@ -8008,7 +8052,7 @@ async function validateCommand(opts, deps = {}) {
|
|
|
8008
8052
|
import { Command, CommanderError, Option } from "commander";
|
|
8009
8053
|
|
|
8010
8054
|
// src/commands/doc.ts
|
|
8011
|
-
import {
|
|
8055
|
+
import { mkdir as mkdir7, readFile as readFile11, writeFile as writeFile4 } from "fs/promises";
|
|
8012
8056
|
import { join as join25 } from "path";
|
|
8013
8057
|
var NUMBERED = Object.keys(STATUSES);
|
|
8014
8058
|
var SECTIONS = {
|
|
@@ -8044,6 +8088,19 @@ async function adrSections(rnessDir) {
|
|
|
8044
8088
|
}
|
|
8045
8089
|
return ["Context", "Decision", "Alternatives Considered", "Consequences"];
|
|
8046
8090
|
}
|
|
8091
|
+
async function writeDocument(file, text) {
|
|
8092
|
+
try {
|
|
8093
|
+
await writeFile4(file, text, { flag: "wx" });
|
|
8094
|
+
} catch (e) {
|
|
8095
|
+
if (e.code !== "EEXIST") throw e;
|
|
8096
|
+
process.stderr.write(`${file} exists; nothing written
|
|
8097
|
+
`);
|
|
8098
|
+
return 1;
|
|
8099
|
+
}
|
|
8100
|
+
process.stdout.write(`${file}
|
|
8101
|
+
`);
|
|
8102
|
+
return 0;
|
|
8103
|
+
}
|
|
8047
8104
|
async function docNewCommand(collection, opts) {
|
|
8048
8105
|
if (!NUMBERED.includes(collection)) {
|
|
8049
8106
|
process.stderr.write(
|
|
@@ -8083,20 +8140,8 @@ async function docNewCommand(collection, opts) {
|
|
|
8083
8140
|
...name === "plans" ? [] : ["", "---"],
|
|
8084
8141
|
...sections.flatMap((s) => ["", `## ${s}`])
|
|
8085
8142
|
];
|
|
8086
|
-
|
|
8087
|
-
await access2(file);
|
|
8088
|
-
process.stderr.write(`${file} exists; nothing written
|
|
8089
|
-
`);
|
|
8090
|
-
return 1;
|
|
8091
|
-
} catch {
|
|
8092
|
-
}
|
|
8093
|
-
await writeFile4(file, `${[...front, "", ...body].join("\n")}
|
|
8094
|
-
`, {
|
|
8095
|
-
flag: "wx"
|
|
8096
|
-
});
|
|
8097
|
-
process.stdout.write(`${file}
|
|
8143
|
+
return await writeDocument(file, `${[...front, "", ...body].join("\n")}
|
|
8098
8144
|
`);
|
|
8099
|
-
return 0;
|
|
8100
8145
|
} catch (e) {
|
|
8101
8146
|
return reportError(e);
|
|
8102
8147
|
}
|
|
@@ -8482,9 +8527,6 @@ function cwdOf(input, env) {
|
|
|
8482
8527
|
return env["CLAUDE_PROJECT_DIR"] ?? process.cwd();
|
|
8483
8528
|
}
|
|
8484
8529
|
var toPosix2 = (p) => p.split(sep5).join(posix8.sep);
|
|
8485
|
-
function count(n, [one, many]) {
|
|
8486
|
-
return `${n} ${n === 1 ? one : many}`;
|
|
8487
|
-
}
|
|
8488
8530
|
async function safetyNet(ws, manifest) {
|
|
8489
8531
|
const notes = [];
|
|
8490
8532
|
const drift = await pinDrift(ws.rnessDir);
|
|
@@ -9876,4 +9918,4 @@ export {
|
|
|
9876
9918
|
OWN_COMMANDS,
|
|
9877
9919
|
catchUp
|
|
9878
9920
|
};
|
|
9879
|
-
//# sourceMappingURL=chunk-
|
|
9921
|
+
//# sourceMappingURL=chunk-JHU2G7K7.js.map
|