@coderook/cli 0.5.0 → 0.7.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/dist/cli/src/api.js +2 -0
- package/dist/cli/src/cli.js +132 -1
- package/dist/desktop-app/src/main/download.js +21 -0
- package/dist/desktop-app/src/main/faults.js +39 -0
- package/dist/desktop-app/src/main/identify.js +40 -0
- package/dist/desktop-app/src/main/upload.js +2 -0
- package/package.json +45 -42
package/dist/cli/src/api.js
CHANGED
|
@@ -10,6 +10,7 @@ exports.resolveMergeConflict = resolveMergeConflict;
|
|
|
10
10
|
exports.applyMerge = applyMerge;
|
|
11
11
|
exports.cancelMerge = cancelMerge;
|
|
12
12
|
/** The small part of the API the command-line tool needs directly. */
|
|
13
|
+
const identify_js_1 = require("../../desktop-app/src/main/identify.js");
|
|
13
14
|
const config_js_1 = require("./config.js");
|
|
14
15
|
async function call(route, options = {}) {
|
|
15
16
|
const token = await (0, config_js_1.loadToken)();
|
|
@@ -22,6 +23,7 @@ async function call(route, options = {}) {
|
|
|
22
23
|
accept: "application/json",
|
|
23
24
|
authorization: `Bearer ${token}`,
|
|
24
25
|
"user-agent": "CodeRook-CLI/0.1",
|
|
26
|
+
...(0, identify_js_1.clientHeaders)(),
|
|
25
27
|
...(options.body ? { "content-type": "application/json" } : {}),
|
|
26
28
|
},
|
|
27
29
|
body: options.body ? JSON.stringify(options.body) : undefined,
|
package/dist/cli/src/cli.js
CHANGED
|
@@ -22,6 +22,8 @@ const api_js_1 = require("./api.js");
|
|
|
22
22
|
const worktree_js_1 = require("../../desktop-app/src/main/worktree.js");
|
|
23
23
|
const upload_js_1 = require("../../desktop-app/src/main/upload.js");
|
|
24
24
|
const download_js_1 = require("../../desktop-app/src/main/download.js");
|
|
25
|
+
const faults_js_1 = require("../../desktop-app/src/main/faults.js");
|
|
26
|
+
const identify_js_1 = require("../../desktop-app/src/main/identify.js");
|
|
25
27
|
const cbx_js_1 = require("../../desktop-app/src/main/cbx.js");
|
|
26
28
|
const api_js_2 = require("./api.js");
|
|
27
29
|
const config_js_1 = require("./config.js");
|
|
@@ -38,6 +40,12 @@ const VERSION = (() => {
|
|
|
38
40
|
return "0.0.0";
|
|
39
41
|
}
|
|
40
42
|
})();
|
|
43
|
+
/*
|
|
44
|
+
Said once, before anything reaches the service. The version comes from the
|
|
45
|
+
package rather than a constant, so it cannot drift from what was published
|
|
46
|
+
and claim a behaviour this build does not have.
|
|
47
|
+
*/
|
|
48
|
+
(0, identify_js_1.declareClient)("cli", VERSION);
|
|
41
49
|
// ── presentation ────────────────────────────────────────────────────────────
|
|
42
50
|
const colour = node_process_1.default.stdout.isTTY && !node_process_1.default.env.NO_COLOR;
|
|
43
51
|
const dim = (text) => (colour ? `[2m${text}[0m` : text);
|
|
@@ -270,9 +278,36 @@ async function commandStatus(parsed) {
|
|
|
270
278
|
async function commandSubmit(parsed) {
|
|
271
279
|
const folder = folderFor(parsed);
|
|
272
280
|
const message = flagText(parsed, "m", "message") ?? "";
|
|
273
|
-
const { link, baseline } = await reconcile(folder);
|
|
281
|
+
const { link, baseline, behind } = await reconcile(folder);
|
|
274
282
|
const rules = await (0, worktree_js_1.readRules)(folder);
|
|
275
283
|
const files = await (0, worktree_js_1.changedFiles)(folder, rules, baseline);
|
|
284
|
+
/*
|
|
285
|
+
An upgraded folder with no materialisation record and something that
|
|
286
|
+
differs from its recorded version is ambiguous in the one way that
|
|
287
|
+
matters: the difference is either work to send, or a copy left behind by
|
|
288
|
+
somebody's merge. Sending the second reverts their change.
|
|
289
|
+
|
|
290
|
+
While the folder is level with the project the question does not arise —
|
|
291
|
+
nothing can have merged underneath it — so only a folder that is behind
|
|
292
|
+
is stopped, and only until somebody says which it is.
|
|
293
|
+
*/
|
|
294
|
+
if (link && !link.local && files.length && behind) {
|
|
295
|
+
if (await establishMaterialisation(folder, link)) {
|
|
296
|
+
link.local = { ...link.manifest };
|
|
297
|
+
await (0, config_js_1.writeLink)(folder, link);
|
|
298
|
+
}
|
|
299
|
+
else if (!hasFlag(parsed, "f", "force")) {
|
|
300
|
+
console.error(red("This folder was connected by an older version of CodeRook,"));
|
|
301
|
+
console.error("which did not record what it had received — and somebody");
|
|
302
|
+
console.error(`has saved since (the project is on v${behind.sequence}).\n`);
|
|
303
|
+
console.error(`${files.length} file${files.length === 1 ? " differs" : "s differ"} here:`);
|
|
304
|
+
for (const file of files.slice(0, 20))
|
|
305
|
+
console.error(` ${file.path}`);
|
|
306
|
+
console.error(`\nIf that is your work, send it with ${accent("coderook submit --force")}.` +
|
|
307
|
+
`\nIf it is an old copy, take theirs with ${accent("coderook get --replace")}.`);
|
|
308
|
+
return 1;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
276
311
|
if (!files.length) {
|
|
277
312
|
// Having nothing to send is not the same as holding the whole version:
|
|
278
313
|
// a merge can leave this folder with an older copy of somebody else's
|
|
@@ -415,6 +450,20 @@ The connection failed: ${text}`));
|
|
|
415
450
|
` · version holds ${bytes(result.sourceBytes)}`);
|
|
416
451
|
return 0;
|
|
417
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Work out what an upgraded folder actually holds, or admit that it cannot.
|
|
455
|
+
*
|
|
456
|
+
* Returns the materialisation when the folder matches its recorded version
|
|
457
|
+
* exactly — which proves the manifest describes what is here — and null when
|
|
458
|
+
* anything differs, because a difference is either somebody's unsaved edit or
|
|
459
|
+
* a copy their merge left stale, and nothing on disk says which.
|
|
460
|
+
*/
|
|
461
|
+
async function establishMaterialisation(folder, link) {
|
|
462
|
+
const rules = await (0, worktree_js_1.readRules)(folder);
|
|
463
|
+
const against = new Map(Object.entries(link.manifest));
|
|
464
|
+
const differing = await (0, worktree_js_1.changedFiles)(folder, rules, against);
|
|
465
|
+
return differing.length ? null : { ...link.manifest };
|
|
466
|
+
}
|
|
418
467
|
async function commandGet(parsed) {
|
|
419
468
|
const folder = folderFor(parsed);
|
|
420
469
|
const { link } = await reconcile(folder);
|
|
@@ -433,7 +482,49 @@ async function commandGet(parsed) {
|
|
|
433
482
|
copy. Only the edits are worth stopping for, and only when the incoming
|
|
434
483
|
version would actually change those files.
|
|
435
484
|
*/
|
|
485
|
+
/*
|
|
486
|
+
A fetch that did not finish leaves the folder part-way to a version, with
|
|
487
|
+
the record still describing what was there before. Every difference it
|
|
488
|
+
shows is this operation's own work, not somebody's edit, so finishing the
|
|
489
|
+
job is the only sensible reading — and refusing would strand the folder in
|
|
490
|
+
the one state that ought to be easiest to leave.
|
|
491
|
+
|
|
492
|
+
It could only have started if there was nothing conflicting here, so
|
|
493
|
+
completing it cannot bury anybody's changes.
|
|
494
|
+
*/
|
|
495
|
+
const resuming = Boolean(link.fetching);
|
|
496
|
+
if (resuming) {
|
|
497
|
+
console.log(dim(`A fetch of v${link.fetching.sequence} did not finish; completing it.`));
|
|
498
|
+
}
|
|
436
499
|
const replacing = hasFlag(parsed, "replace", "f", "force");
|
|
500
|
+
/*
|
|
501
|
+
A folder connected by a client older than the materialisation record has
|
|
502
|
+
only the Version manifest, which says what the project held — not what
|
|
503
|
+
was placed here. The two are the same only until somebody's merge makes
|
|
504
|
+
them differ, so assuming it would be the very guess that lost work in the
|
|
505
|
+
first place.
|
|
506
|
+
|
|
507
|
+
It can be established rather than assumed: if the folder matches its own
|
|
508
|
+
recorded version exactly, then it demonstrably holds that version and the
|
|
509
|
+
manifest is the materialisation. If it does not match, an edit cannot be
|
|
510
|
+
told from a stale copy, and fetching is refused until somebody says which
|
|
511
|
+
it is.
|
|
512
|
+
*/
|
|
513
|
+
if (!link.local) {
|
|
514
|
+
const settled = await establishMaterialisation(folder, link);
|
|
515
|
+
if (settled) {
|
|
516
|
+
link.local = settled;
|
|
517
|
+
await (0, config_js_1.writeLink)(folder, link);
|
|
518
|
+
}
|
|
519
|
+
else if (!replacing) {
|
|
520
|
+
console.error(red("This folder was connected by an older version of CodeRook,"));
|
|
521
|
+
console.error("which did not record what it had received, so changed files here");
|
|
522
|
+
console.error("cannot be told apart from copies left behind by a merge.\n");
|
|
523
|
+
console.error(`Save them with ${accent("coderook submit")} if they are your work, or ` +
|
|
524
|
+
`take the\nsaved version exactly with ${accent("coderook get --replace")}.`);
|
|
525
|
+
return 1;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
437
528
|
if (link.local && !replacing) {
|
|
438
529
|
const rules = await (0, worktree_js_1.readRules)(folder);
|
|
439
530
|
const edited = await (0, worktree_js_1.changedFiles)(folder, rules, new Map(Object.entries(link.local)));
|
|
@@ -446,6 +537,31 @@ async function commandGet(parsed) {
|
|
|
446
537
|
file.sha256,
|
|
447
538
|
])
|
|
448
539
|
: []);
|
|
540
|
+
/*
|
|
541
|
+
Resuming needs a different question. Half these files differ from the
|
|
542
|
+
record because the interrupted run already replaced them, and those
|
|
543
|
+
are finished work rather than anybody's edit. What matters is a file
|
|
544
|
+
that matches neither what was here nor what is arriving — that one was
|
|
545
|
+
changed by hand while the fetch was stopped, and completing over it
|
|
546
|
+
would bury the only copy.
|
|
547
|
+
*/
|
|
548
|
+
if (resuming) {
|
|
549
|
+
const notYetArrived = await (0, worktree_js_1.changedFiles)(folder, rules, incoming);
|
|
550
|
+
const changedInTheGap = notYetArrived.filter((file) => !file.deleted &&
|
|
551
|
+
incoming.has(file.path) &&
|
|
552
|
+
edited.some((one) => one.path === file.path));
|
|
553
|
+
if (!changedInTheGap.length) {
|
|
554
|
+
return fetchInto(link.repositoryId, folder, link.slug, link.local ?? null, "replace");
|
|
555
|
+
}
|
|
556
|
+
console.error(red(`${changedInTheGap.length} file${changedInTheGap.length === 1 ? "" : "s"} ` +
|
|
557
|
+
`changed while the interrupted fetch was stopped:`));
|
|
558
|
+
for (const file of changedInTheGap.slice(0, 20))
|
|
559
|
+
console.error(` ${file.path}`);
|
|
560
|
+
console.error(`
|
|
561
|
+
Save them with ${accent("coderook submit")}, or finish the fetch and ` +
|
|
562
|
+
`discard them with ${accent("coderook get --replace")}.`);
|
|
563
|
+
return 1;
|
|
564
|
+
}
|
|
449
565
|
/*
|
|
450
566
|
Only a genuine collision stops the fetch: this folder changed the
|
|
451
567
|
file and so did the incoming version. An edit to a file the version
|
|
@@ -508,6 +624,18 @@ held, mode = "replace") {
|
|
|
508
624
|
console.error(red("That project has no saved version."));
|
|
509
625
|
return 1;
|
|
510
626
|
}
|
|
627
|
+
/*
|
|
628
|
+
Recorded before the folder is touched. If this run does not reach the
|
|
629
|
+
end, the next one finds this and knows the differences it sees are its
|
|
630
|
+
own unfinished work rather than somebody's edits.
|
|
631
|
+
*/
|
|
632
|
+
const before = await (0, config_js_1.readLink)(destination);
|
|
633
|
+
if (before) {
|
|
634
|
+
await (0, config_js_1.writeLink)(destination, {
|
|
635
|
+
...before,
|
|
636
|
+
fetching: { versionId: latest.id, sequence: latest.sequence },
|
|
637
|
+
});
|
|
638
|
+
}
|
|
511
639
|
const line = progressLine();
|
|
512
640
|
const result = await downloader.run(repositoryId, latest.id, destination, (progress) => {
|
|
513
641
|
line(` ${String(progress.percent).padStart(3)}% ${progress.files}/${progress.totalFiles}` +
|
|
@@ -527,7 +655,10 @@ held, mode = "replace") {
|
|
|
527
655
|
// version holds and the two agree again.
|
|
528
656
|
local: result.manifest,
|
|
529
657
|
manifest: result.manifest,
|
|
658
|
+
// Reached the end, so there is no longer a fetch in flight.
|
|
530
659
|
});
|
|
660
|
+
// The record is written; the run has not reported success yet.
|
|
661
|
+
(0, faults_js_1.maybeFail)("get:after-record");
|
|
531
662
|
console.log(`${accent(`v${latest.sequence}`)} · ${result.files} files · ${bytes(result.bytes)} into ${destination}`);
|
|
532
663
|
return 0;
|
|
533
664
|
}
|
|
@@ -15,6 +15,8 @@ exports.Downloader = exports.DownloadCancelled = void 0;
|
|
|
15
15
|
const node_crypto_1 = require("node:crypto");
|
|
16
16
|
const promises_1 = require("node:fs/promises");
|
|
17
17
|
const node_path_1 = __importDefault(require("node:path"));
|
|
18
|
+
const identify_js_1 = require("./identify.js");
|
|
19
|
+
const faults_js_1 = require("./faults.js");
|
|
18
20
|
async function exists(target) {
|
|
19
21
|
try {
|
|
20
22
|
await (0, promises_1.stat)(target);
|
|
@@ -83,11 +85,21 @@ class Downloader {
|
|
|
83
85
|
headers: {
|
|
84
86
|
authorization: `Bearer ${token}`,
|
|
85
87
|
"user-agent": "CodeRook/0.1",
|
|
88
|
+
...(0, identify_js_1.clientHeaders)(),
|
|
86
89
|
},
|
|
87
90
|
signal: this.controller.signal,
|
|
88
91
|
});
|
|
89
92
|
if (!response.ok) {
|
|
90
93
|
const text = await response.text().catch(() => "");
|
|
94
|
+
/*
|
|
95
|
+
A refusal for being too old is not a network failure and must not read
|
|
96
|
+
like one: the person can fix it, and only if they are told how.
|
|
97
|
+
*/
|
|
98
|
+
const tooOld = (0, identify_js_1.readRefusal)(response.status, text);
|
|
99
|
+
if (tooOld) {
|
|
100
|
+
throw new Error(`${tooOld.message} You are running ${tooOld.yourVersion ?? "an older build"}; ` +
|
|
101
|
+
`update from ${tooOld.upgradeUrl}.`);
|
|
102
|
+
}
|
|
91
103
|
let message = `${route} failed (${response.status})`;
|
|
92
104
|
try {
|
|
93
105
|
message = JSON.parse(text)?.error?.message ?? message;
|
|
@@ -187,7 +199,10 @@ class Downloader {
|
|
|
187
199
|
one volume is atomic, so no file is ever seen half-written, and
|
|
188
200
|
anything the version does not mention is left exactly as it is.
|
|
189
201
|
*/
|
|
202
|
+
// Everything has arrived and verified; the folder is still untouched.
|
|
203
|
+
(0, faults_js_1.maybeFail)("get:after-staging");
|
|
190
204
|
await (0, promises_1.mkdir)(destination, { recursive: true });
|
|
205
|
+
let placed = 0;
|
|
191
206
|
for (const file of files) {
|
|
192
207
|
const parts = file.path.split("/").filter(Boolean);
|
|
193
208
|
const target = node_path_1.default.join(destination, ...parts);
|
|
@@ -206,6 +221,9 @@ class Downloader {
|
|
|
206
221
|
await (0, promises_1.mkdir)(node_path_1.default.dirname(target), { recursive: true });
|
|
207
222
|
await (0, promises_1.rm)(target, { recursive: true, force: true });
|
|
208
223
|
await (0, promises_1.rename)(node_path_1.default.join(staging, ...parts), target);
|
|
224
|
+
placed += 1;
|
|
225
|
+
if (placed === 1)
|
|
226
|
+
(0, faults_js_1.maybeFail)("get:after-first-file");
|
|
209
227
|
}
|
|
210
228
|
/*
|
|
211
229
|
A file this folder received but the version no longer holds is gone
|
|
@@ -229,7 +247,10 @@ class Downloader {
|
|
|
229
247
|
continue;
|
|
230
248
|
await (0, promises_1.rm)(target, { force: true });
|
|
231
249
|
await pruneEmpty(destination, target);
|
|
250
|
+
(0, faults_js_1.maybeFail)("get:after-delete");
|
|
232
251
|
}
|
|
252
|
+
// The folder is now the new version; nothing has recorded that yet.
|
|
253
|
+
(0, faults_js_1.maybeFail)("get:before-record");
|
|
233
254
|
await (0, promises_1.rm)(staging, { recursive: true, force: true });
|
|
234
255
|
report({
|
|
235
256
|
files: written,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Deliberate failure points, for proving that interruption recovers.
|
|
4
|
+
*
|
|
5
|
+
* Every operation that touches a folder is a sequence of individually correct
|
|
6
|
+
* steps, and the dangerous state is between two of them: half the files
|
|
7
|
+
* replaced, a deletion applied but the record not yet written, a version
|
|
8
|
+
* committed but never learned about. Those states cannot be reached by
|
|
9
|
+
* ordinary testing, because ordinary testing runs the sequence to the end.
|
|
10
|
+
*
|
|
11
|
+
* So the sequence can be cut, at named points, by setting an environment
|
|
12
|
+
* variable. Nothing here does anything at all unless that variable names a
|
|
13
|
+
* point — there is no configuration, no flag and no code path that reaches it
|
|
14
|
+
* otherwise, which is what makes it safe to leave in the shipped build rather
|
|
15
|
+
* than maintaining a separate instrumented one that nobody ever runs.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.InjectedFault = void 0;
|
|
19
|
+
exports.maybeFail = maybeFail;
|
|
20
|
+
class InjectedFault extends Error {
|
|
21
|
+
point;
|
|
22
|
+
constructor(point) {
|
|
23
|
+
super(`Interrupted deliberately at ${point}`);
|
|
24
|
+
this.point = point;
|
|
25
|
+
this.name = "InjectedFault";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.InjectedFault = InjectedFault;
|
|
29
|
+
/**
|
|
30
|
+
* Stop here, if this is the point somebody asked to be interrupted at.
|
|
31
|
+
*
|
|
32
|
+
* Reads the environment on every call rather than caching it, so a test can
|
|
33
|
+
* drive several runs in one process without the first one deciding for all
|
|
34
|
+
* the others.
|
|
35
|
+
*/
|
|
36
|
+
function maybeFail(point) {
|
|
37
|
+
if (process.env.CODEROOK_FAULT === point)
|
|
38
|
+
throw new InjectedFault(point);
|
|
39
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.declareClient = declareClient;
|
|
4
|
+
exports.clientHeaders = clientHeaders;
|
|
5
|
+
exports.readRefusal = readRefusal;
|
|
6
|
+
/** Set once at start-up, because the two clients learn their version differently. */
|
|
7
|
+
let identity = "";
|
|
8
|
+
function declareClient(kind, version) {
|
|
9
|
+
identity = `${kind}/${version}`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The identifying headers, or nothing when start-up has not declared yet.
|
|
13
|
+
*
|
|
14
|
+
* Returning nothing rather than a guess matters: an unrecognised client is
|
|
15
|
+
* allowed through, so a wrong guess would be worse than silence — it would
|
|
16
|
+
* claim a version whose behaviour this build does not have.
|
|
17
|
+
*/
|
|
18
|
+
function clientHeaders() {
|
|
19
|
+
return identity ? { "x-coderook-client": identity } : {};
|
|
20
|
+
}
|
|
21
|
+
function readRefusal(status, body) {
|
|
22
|
+
if (status !== 426)
|
|
23
|
+
return null;
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(body);
|
|
26
|
+
const error = parsed.error;
|
|
27
|
+
if (!error || error.code !== "client_too_old")
|
|
28
|
+
return null;
|
|
29
|
+
return {
|
|
30
|
+
capability: String(error.capability ?? "that"),
|
|
31
|
+
minimumSupported: String(error.minimumSupported ?? "a newer version"),
|
|
32
|
+
yourVersion: error.yourVersion ? String(error.yourVersion) : null,
|
|
33
|
+
upgradeUrl: String(error.upgradeUrl ?? "https://coderook.com/download"),
|
|
34
|
+
message: String(error.message ?? "This version is too old."),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -16,6 +16,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
16
16
|
const node_fs_1 = require("node:fs");
|
|
17
17
|
const promises_1 = require("node:fs/promises");
|
|
18
18
|
const node_path_1 = __importDefault(require("node:path"));
|
|
19
|
+
const identify_js_1 = require("./identify.js");
|
|
19
20
|
const worktree_js_1 = require("./worktree.js");
|
|
20
21
|
const publish_name_js_1 = require("./publish_name.js");
|
|
21
22
|
/** Above this the API insists on a multipart session. */
|
|
@@ -87,6 +88,7 @@ class Uploader {
|
|
|
87
88
|
accept: "application/json",
|
|
88
89
|
authorization: `Bearer ${token}`,
|
|
89
90
|
"user-agent": "CodeRook/0.1",
|
|
91
|
+
...(0, identify_js_1.clientHeaders)(),
|
|
90
92
|
...(init.contentType ? { "content-type": init.contentType } : {}),
|
|
91
93
|
},
|
|
92
94
|
body: init.body,
|
package/package.json
CHANGED
|
@@ -1,42 +1,45 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@coderook/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "CodeRook from the command line, on any operating system",
|
|
5
|
-
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
|
-
"homepage": "https://coderook.com",
|
|
7
|
-
"bugs": {
|
|
8
|
-
"url": "https://coderook.com/contact"
|
|
9
|
-
},
|
|
10
|
-
"keywords": [
|
|
11
|
-
"coderook",
|
|
12
|
-
"versioning",
|
|
13
|
-
"backup",
|
|
14
|
-
"cbx"
|
|
15
|
-
],
|
|
16
|
-
"engines": {
|
|
17
|
-
"node": ">=20.11.0"
|
|
18
|
-
},
|
|
19
|
-
"bin": {
|
|
20
|
-
"coderook": "dist/cli/src/cli.js"
|
|
21
|
-
},
|
|
22
|
-
"files": [
|
|
23
|
-
"dist"
|
|
24
|
-
],
|
|
25
|
-
"scripts": {
|
|
26
|
-
"build": "tsc -p tsconfig.json",
|
|
27
|
-
"check": "tsc -p tsconfig.json --noEmit",
|
|
28
|
-
"test": "tsc -p tsconfig.json && node --test --experimental-strip-types test/*.test.ts",
|
|
29
|
-
"start": "node dist/cli/src/cli.js",
|
|
30
|
-
"prepublishOnly": "npm run build",
|
|
31
|
-
"test:e2e": "node test/e2e.mjs",
|
|
32
|
-
"test:matrix": "node test/state-matrix.mjs",
|
|
33
|
-
"test:attempt": "node test/attempt-identity.mjs",
|
|
34
|
-
"test:get": "node test/get-safety.mjs",
|
|
35
|
-
"test:live": "node test/e2e.mjs --allow-production && node test/state-matrix.mjs --allow-production && node test/get-safety.mjs --allow-production && node test/get-protects-edits.mjs --allow-production && node test/attempt-identity.mjs --allow-production",
|
|
36
|
-
"test:getedits": "node test/get-protects-edits.mjs"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@coderook/cli",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "CodeRook from the command line, on any operating system",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
|
+
"homepage": "https://coderook.com",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://coderook.com/contact"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"coderook",
|
|
12
|
+
"versioning",
|
|
13
|
+
"backup",
|
|
14
|
+
"cbx"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=20.11.0"
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"coderook": "dist/cli/src/cli.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc -p tsconfig.json",
|
|
27
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
28
|
+
"test": "tsc -p tsconfig.json && node --test --experimental-strip-types test/*.test.ts",
|
|
29
|
+
"start": "node dist/cli/src/cli.js",
|
|
30
|
+
"prepublishOnly": "npm run build",
|
|
31
|
+
"test:e2e": "node test/e2e.mjs",
|
|
32
|
+
"test:matrix": "node test/state-matrix.mjs",
|
|
33
|
+
"test:attempt": "node test/attempt-identity.mjs",
|
|
34
|
+
"test:get": "node test/get-safety.mjs",
|
|
35
|
+
"test:live": "node test/e2e.mjs --allow-production && node test/state-matrix.mjs --allow-production && node test/get-safety.mjs --allow-production && node test/get-protects-edits.mjs --allow-production && node test/upgrade-migration.mjs --allow-production && node test/fault-get.mjs --allow-production && node test/version-floor.mjs --allow-production && node test/attempt-identity.mjs --allow-production",
|
|
36
|
+
"test:getedits": "node test/get-protects-edits.mjs",
|
|
37
|
+
"test:upgrade": "node test/upgrade-migration.mjs",
|
|
38
|
+
"test:faultget": "node test/fault-get.mjs",
|
|
39
|
+
"test:floor": "node test/version-floor.mjs"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "24.10.1",
|
|
43
|
+
"typescript": "5.9.3"
|
|
44
|
+
}
|
|
45
|
+
}
|