@coderook/cli 0.9.0 → 0.10.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 +157 -0
- package/dist/cli/src/cli.js +430 -63
- package/dist/cli/src/help.js +88 -0
- package/dist/cli/src/project_commands.js +150 -0
- package/dist/cli/src/registry.js +73 -0
- package/dist/cli/src/runner.js +423 -0
- package/dist/cli/src/service_commands.js +238 -0
- package/package.json +48 -47
package/dist/cli/src/cli.js
CHANGED
|
@@ -16,9 +16,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
16
16
|
* version, submit it.
|
|
17
17
|
*/
|
|
18
18
|
const promises_1 = require("node:readline/promises");
|
|
19
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
19
20
|
const node_path_1 = __importDefault(require("node:path"));
|
|
20
21
|
const node_process_1 = __importDefault(require("node:process"));
|
|
21
22
|
const api_js_1 = require("./api.js");
|
|
23
|
+
const registry_js_1 = require("./registry.js");
|
|
24
|
+
const help_js_1 = require("./help.js");
|
|
25
|
+
const project_commands_js_1 = require("./project_commands.js");
|
|
26
|
+
const service_commands_js_1 = require("./service_commands.js");
|
|
22
27
|
const worktree_js_1 = require("../../desktop-app/src/main/worktree.js");
|
|
23
28
|
const upload_js_1 = require("../../desktop-app/src/main/upload.js");
|
|
24
29
|
const download_js_1 = require("../../desktop-app/src/main/download.js");
|
|
@@ -26,6 +31,7 @@ const faults_js_1 = require("../../desktop-app/src/main/faults.js");
|
|
|
26
31
|
const identify_js_1 = require("../../desktop-app/src/main/identify.js");
|
|
27
32
|
const cbx_js_1 = require("../../desktop-app/src/main/cbx.js");
|
|
28
33
|
const api_js_2 = require("./api.js");
|
|
34
|
+
const runner_js_1 = require("./runner.js");
|
|
29
35
|
const config_js_1 = require("./config.js");
|
|
30
36
|
/*
|
|
31
37
|
Read from the package rather than written twice. A hardcoded copy had
|
|
@@ -52,6 +58,7 @@ const dim = (text) => (colour ? `[2m${text}[0m` : text);
|
|
|
52
58
|
const bold = (text) => (colour ? `[1m${text}[0m` : text);
|
|
53
59
|
const accent = (text) => (colour ? `[33m${text}[0m` : text);
|
|
54
60
|
const red = (text) => (colour ? `[31m${text}[0m` : text);
|
|
61
|
+
const green = (text) => (colour ? `[32m${text}[0m` : text);
|
|
55
62
|
function bytes(value) {
|
|
56
63
|
if (value >= 1024 ** 3)
|
|
57
64
|
return `${(value / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -767,42 +774,6 @@ async function commandDoctor() {
|
|
|
767
774
|
}
|
|
768
775
|
return 0;
|
|
769
776
|
}
|
|
770
|
-
const USAGE = `${bold("coderook")} — CodeRook from the command line
|
|
771
|
-
|
|
772
|
-
${bold("Getting started")}
|
|
773
|
-
coderook sign-in Store a personal access token
|
|
774
|
-
coderook whoami Who this machine is signed in as
|
|
775
|
-
coderook projects Every project on your account
|
|
776
|
-
|
|
777
|
-
${bold("Working with a folder")}
|
|
778
|
-
coderook status [folder] What is here that is not saved yet
|
|
779
|
-
coderook submit [folder] -m "…" Send the changes as a new version
|
|
780
|
-
coderook get [folder] Bring this folder up to date
|
|
781
|
-
(--replace for an exact copy)
|
|
782
|
-
coderook clone <project> [dir] Fetch a project into a new folder
|
|
783
|
-
coderook rules [folder] Show the ignore rules (--init to start one)
|
|
784
|
-
|
|
785
|
-
${bold("When somebody saved first")}
|
|
786
|
-
coderook merges Uploads of yours waiting on a decision
|
|
787
|
-
coderook merge <ref> Look at one (--mine --theirs --both --drop)
|
|
788
|
-
|
|
789
|
-
${bold("Bundles")}
|
|
790
|
-
coderook bundle [folder] [out] Pack the project as a .cbx
|
|
791
|
-
coderook unbundle <file> [dir] Extract a .cbx
|
|
792
|
-
coderook inspect <file> What a .cbx contains (--files to list them)
|
|
793
|
-
|
|
794
|
-
${bold("Other")}
|
|
795
|
-
coderook doctor Check the service, config and sign-in
|
|
796
|
-
coderook sign-out Forget the token on this machine
|
|
797
|
-
|
|
798
|
-
${bold("Options")}
|
|
799
|
-
-m, --message The version message for submit
|
|
800
|
-
-n, --dry-run Show what submit would send, without sending
|
|
801
|
-
--allow-secrets Send files that look like credentials anyway
|
|
802
|
-
--token Supply the token to sign-in instead of being asked
|
|
803
|
-
|
|
804
|
-
${dim("The environment variable CODEROOK_TOKEN is used when set, so automated")}
|
|
805
|
-
${dim("runs need nothing on disk. CODEROOK_API_URL points at another service.")}`;
|
|
806
777
|
/**
|
|
807
778
|
* Everything waiting on a decision, for one folder's project.
|
|
808
779
|
*
|
|
@@ -924,43 +895,439 @@ Run coderook merge ${now.mergeTrack.reference} --apply to publish it.`));
|
|
|
924
895
|
}
|
|
925
896
|
return 0;
|
|
926
897
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
898
|
+
/**
|
|
899
|
+
* Do this project's work on this machine.
|
|
900
|
+
*
|
|
901
|
+
* The other half of Actions. CodeRook writes down that a run is wanted; this
|
|
902
|
+
* asks whether there is one, fetches the version into a throwaway folder,
|
|
903
|
+
* runs the workflow's command there, sends the output up as it goes, and
|
|
904
|
+
* reports the verdict.
|
|
905
|
+
*
|
|
906
|
+
* The trust boundary is said out loud rather than buried in documentation,
|
|
907
|
+
* because it is the thing somebody should decide on purpose: anybody who can
|
|
908
|
+
* set a workflow's command on this project can run that command on this
|
|
909
|
+
* machine, as whoever started this.
|
|
910
|
+
*/
|
|
911
|
+
async function commandRunner(parsed) {
|
|
912
|
+
const reference = parsed.positional[0] ?? flagText(parsed, "project", "p");
|
|
913
|
+
if (!reference) {
|
|
914
|
+
console.error(red("Which project? coderook runner <project>"));
|
|
915
|
+
return 1;
|
|
916
|
+
}
|
|
917
|
+
const project = await (0, api_js_2.findProject)(reference);
|
|
918
|
+
if (!project) {
|
|
919
|
+
console.error(red(`No project of yours matches "${reference}".`));
|
|
920
|
+
return 1;
|
|
921
|
+
}
|
|
922
|
+
const name = flagText(parsed, "name") ?? node_os_1.default.hostname();
|
|
923
|
+
/*
|
|
924
|
+
What this machine will answer to. Its own platform unless told otherwise,
|
|
925
|
+
because that is what somebody means when they write a workflow that has to
|
|
926
|
+
produce a Windows installer.
|
|
927
|
+
*/
|
|
928
|
+
const labels = (flagText(parsed, "labels", "label") ?? "")
|
|
929
|
+
.split(",")
|
|
930
|
+
.map((one) => one.trim())
|
|
931
|
+
.filter(Boolean);
|
|
932
|
+
const answersTo = labels.length ? labels : (0, runner_js_1.defaultLabels)();
|
|
933
|
+
const once = hasFlag(parsed, "once");
|
|
934
|
+
const every = Math.min(Math.max(Number(flagText(parsed, "poll") ?? 5), 1), 300);
|
|
935
|
+
console.log(`${bold("CodeRook runner")} ${dim(`· ${name}`)}`);
|
|
936
|
+
console.log(`Taking work for ${accent(project.slug)} ` +
|
|
937
|
+
dim(`· answering to ${answersTo.join(", ")}`));
|
|
938
|
+
/*
|
|
939
|
+
Printed every time rather than once on first use. Somebody who left this
|
|
940
|
+
running in a terminal a fortnight ago should be able to look at it and
|
|
941
|
+
see what it is permitted to do, without going to find the manual.
|
|
942
|
+
*/
|
|
943
|
+
console.log(dim("Anyone who can set a workflow's command on this project can run it " +
|
|
944
|
+
"here, as you."));
|
|
945
|
+
/*
|
|
946
|
+
Said separately because it is the part people get wrong. The account token
|
|
947
|
+
is kept out of the command's environment, but a command runs as this user
|
|
948
|
+
and this user can read the token file — so the honest advice is a token
|
|
949
|
+
made for the runner, which can be revoked without taking the desktop
|
|
950
|
+
application and the command line down with it.
|
|
951
|
+
*/
|
|
952
|
+
console.log(dim(`A command here runs as you and can read ${(0, config_js_1.configDirectory)()}. ` +
|
|
953
|
+
"Use a token minted for this machine, not your everyday one."));
|
|
954
|
+
console.log(dim("Stop with Ctrl-C."));
|
|
955
|
+
if (once)
|
|
956
|
+
console.log(dim("Taking one job, then stopping."));
|
|
957
|
+
console.log("");
|
|
958
|
+
let stopping = false;
|
|
959
|
+
node_process_1.default.on("SIGINT", () => {
|
|
960
|
+
if (stopping)
|
|
961
|
+
node_process_1.default.exit(130);
|
|
962
|
+
stopping = true;
|
|
963
|
+
console.log("");
|
|
964
|
+
console.log(dim("Finishing the current job, then stopping."));
|
|
965
|
+
});
|
|
966
|
+
for (;;) {
|
|
967
|
+
let job = null;
|
|
968
|
+
try {
|
|
969
|
+
job = await (0, runner_js_1.claim)(project.id, name, VERSION, answersTo);
|
|
970
|
+
}
|
|
971
|
+
catch (error) {
|
|
972
|
+
/*
|
|
973
|
+
Kept going rather than exiting. A runner is meant to be left alone,
|
|
974
|
+
and the network being briefly unavailable is not a reason to need
|
|
975
|
+
somebody to come back and start it again.
|
|
976
|
+
*/
|
|
977
|
+
console.error(red(error instanceof Error ? error.message : String(error)));
|
|
978
|
+
if (once)
|
|
979
|
+
return 1;
|
|
980
|
+
await new Promise((wake) => setTimeout(wake, every * 1000));
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
if (!job) {
|
|
984
|
+
if (once || stopping) {
|
|
985
|
+
if (once)
|
|
986
|
+
console.log(dim("Nothing waiting."));
|
|
987
|
+
return 0;
|
|
988
|
+
}
|
|
989
|
+
await new Promise((wake) => setTimeout(wake, every * 1000));
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
const label = `#${job.number} ${job.workflowName}`;
|
|
993
|
+
console.log(`${accent("▶")} ${label}${job.attempts > 1 ? dim(` (attempt ${job.attempts})`) : ""}`);
|
|
994
|
+
const began = Date.now();
|
|
995
|
+
const verdict = await (0, runner_js_1.performRun)(project.id, job, name);
|
|
996
|
+
try {
|
|
997
|
+
await (0, runner_js_1.report)(project.id, job.runId, verdict, Date.now() - began);
|
|
998
|
+
}
|
|
999
|
+
catch (error) {
|
|
1000
|
+
console.error(red(`Could not report ${label}: ` +
|
|
1001
|
+
(error instanceof Error ? error.message : String(error))));
|
|
1002
|
+
}
|
|
1003
|
+
console.log(verdict.status === "passed"
|
|
1004
|
+
? `${green("✓")} ${label} — ${verdict.summary}`
|
|
1005
|
+
: `${red("×")} ${label} — ${verdict.summary}`);
|
|
1006
|
+
if (once || stopping)
|
|
1007
|
+
return verdict.status === "passed" ? 0 : 1;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
/*
|
|
1011
|
+
The commands, described where they are defined.
|
|
1012
|
+
|
|
1013
|
+
This replaced a plain name-to-function map sitting beside a hand-written
|
|
1014
|
+
usage string. The two drifted, as they always do — the help is now generated
|
|
1015
|
+
from these rows, so a command that exists is documented and a command that is
|
|
1016
|
+
documented exists.
|
|
1017
|
+
*/
|
|
1018
|
+
const SPECS = [
|
|
1019
|
+
{
|
|
1020
|
+
name: "sign-in",
|
|
1021
|
+
aliases: ["login"],
|
|
1022
|
+
group: "Getting started",
|
|
1023
|
+
summary: "store a personal access token on this machine",
|
|
1024
|
+
usage: "sign-in",
|
|
1025
|
+
detail: "Asks for a personal access token and saves it. Create one in your\n" +
|
|
1026
|
+
"account settings on the website. The token is stored in your user\n" +
|
|
1027
|
+
"configuration, not in the project folder, so it never lands in a\n" +
|
|
1028
|
+
"version by accident.",
|
|
1029
|
+
options: [
|
|
1030
|
+
{ flags: "--token <value>", description: "supply it instead of being asked" },
|
|
1031
|
+
],
|
|
1032
|
+
run: commandSignIn,
|
|
1033
|
+
},
|
|
1034
|
+
{
|
|
1035
|
+
name: "whoami",
|
|
1036
|
+
group: "Getting started",
|
|
1037
|
+
summary: "who this machine is signed in as",
|
|
1038
|
+
usage: "whoami",
|
|
1039
|
+
run: () => commandWhoami(),
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
name: "sign-out",
|
|
1043
|
+
aliases: ["logout"],
|
|
1044
|
+
group: "Getting started",
|
|
1045
|
+
summary: "forget the token on this machine",
|
|
1046
|
+
usage: "sign-out",
|
|
1047
|
+
run: commandSignOut,
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
name: "status",
|
|
1051
|
+
group: "Working with a folder",
|
|
1052
|
+
summary: "what is here that is not saved yet",
|
|
1053
|
+
usage: "status [folder]",
|
|
1054
|
+
detail: "Compares the folder against the last version you sent and lists what\n" +
|
|
1055
|
+
"changed. Says nothing about other people's work — use `merges` for that.",
|
|
1056
|
+
run: commandStatus,
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
name: "submit",
|
|
1060
|
+
aliases: ["publish"],
|
|
1061
|
+
group: "Working with a folder",
|
|
1062
|
+
summary: "send the changes as a new version",
|
|
1063
|
+
usage: 'submit [folder] -m "…"',
|
|
1064
|
+
detail: "Sends everything that changed since the last version. If the folder is\n" +
|
|
1065
|
+
"not linked to a project yet, one is created on your account, named\n" +
|
|
1066
|
+
"after the folder and private to begin with.",
|
|
1067
|
+
options: [
|
|
1068
|
+
{ flags: "-m, --message <text>", description: "what changed, in a sentence" },
|
|
1069
|
+
{ flags: "-n, --dry-run", description: "show what would be sent, send nothing" },
|
|
1070
|
+
{ flags: "--allow-secrets", description: "send files that look like credentials" },
|
|
1071
|
+
],
|
|
1072
|
+
examples: ['coderook submit -m "Fix the export dialog"'],
|
|
1073
|
+
run: commandSubmit,
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
name: "get",
|
|
1077
|
+
group: "Working with a folder",
|
|
1078
|
+
summary: "bring this folder up to date",
|
|
1079
|
+
usage: "get [folder]",
|
|
1080
|
+
options: [
|
|
1081
|
+
{ flags: "--replace", description: "make it an exact copy, discarding local changes" },
|
|
1082
|
+
],
|
|
1083
|
+
run: commandGet,
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
name: "clone",
|
|
1087
|
+
group: "Working with a folder",
|
|
1088
|
+
summary: "fetch a project into a new folder",
|
|
1089
|
+
usage: "clone <project> [dir]",
|
|
1090
|
+
run: commandClone,
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
name: "ignore",
|
|
1094
|
+
aliases: ["rules"],
|
|
1095
|
+
group: "Working with a folder",
|
|
1096
|
+
summary: "show the ignore rules for this folder",
|
|
1097
|
+
usage: "ignore [folder]",
|
|
1098
|
+
detail: "The local list of things not to send — build output, dependencies,\n" +
|
|
1099
|
+
"anything private. Unrelated to a project's rules on the service, which\n" +
|
|
1100
|
+
"are about who may do what.",
|
|
1101
|
+
options: [{ flags: "--init", description: "write a starting set of rules" }],
|
|
1102
|
+
run: commandRules,
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
name: "projects",
|
|
1106
|
+
group: "Your projects",
|
|
1107
|
+
summary: "every project on your account",
|
|
1108
|
+
usage: "projects",
|
|
1109
|
+
run: () => commandProjects(),
|
|
1110
|
+
},
|
|
1111
|
+
{
|
|
1112
|
+
name: "versions",
|
|
1113
|
+
group: "Your projects",
|
|
1114
|
+
summary: "what has been saved to a project",
|
|
1115
|
+
usage: "versions [project]",
|
|
1116
|
+
detail: "Newest first. Run it inside a linked folder to leave the name out.",
|
|
1117
|
+
options: [{ flags: "--limit <n>", description: "how many to show (default 20)" }],
|
|
1118
|
+
run: project_commands_js_1.commandVersions,
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
name: "ai",
|
|
1122
|
+
group: "Your projects",
|
|
1123
|
+
summary: "what this project says about machines",
|
|
1124
|
+
usage: "ai [project] [--read off]",
|
|
1125
|
+
detail: "Shows the four answers a project gives about automated clients, and\n" +
|
|
1126
|
+
"changes them. Everything is allowed until you say otherwise.\n\n" +
|
|
1127
|
+
"Turning reading off is the one that refuses model training: it declares\n" +
|
|
1128
|
+
"the project as not-for-training in the page, in the response headers,\n" +
|
|
1129
|
+
"and in the service's robots.txt. Turning downloads off does not — those\n" +
|
|
1130
|
+
"are separate questions and answering one says nothing about the other.",
|
|
1131
|
+
options: [
|
|
1132
|
+
{ flags: "--read [off]", description: "reading, indexing, summarising, training" },
|
|
1133
|
+
{ flags: "--download [off]", description: "taking the files" },
|
|
1134
|
+
{ flags: "--contribute [off]", description: "automated contributions" },
|
|
1135
|
+
{ flags: "--request [off]", description: "automated calls to its endpoints" },
|
|
1136
|
+
],
|
|
1137
|
+
examples: [
|
|
1138
|
+
"coderook ai my-project",
|
|
1139
|
+
"coderook ai my-project --read off",
|
|
1140
|
+
"coderook ai my-project --download off --request off",
|
|
1141
|
+
],
|
|
1142
|
+
run: project_commands_js_1.commandAi,
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
name: "issues",
|
|
1146
|
+
group: "Your projects",
|
|
1147
|
+
summary: "issues on a project, or open one",
|
|
1148
|
+
usage: "issues [project]",
|
|
1149
|
+
options: [
|
|
1150
|
+
{ flags: '--new "<title>"', description: "open a new issue" },
|
|
1151
|
+
{ flags: "--body <text>", description: "the description for a new one" },
|
|
1152
|
+
],
|
|
1153
|
+
examples: ['coderook issues my-project --new "Crash on export"'],
|
|
1154
|
+
run: service_commands_js_1.commandIssues,
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
name: "releases",
|
|
1158
|
+
group: "Your projects",
|
|
1159
|
+
summary: "what has been released, and its files",
|
|
1160
|
+
usage: "releases [project]",
|
|
1161
|
+
run: service_commands_js_1.commandReleases,
|
|
1162
|
+
},
|
|
1163
|
+
{
|
|
1164
|
+
name: "actions",
|
|
1165
|
+
aliases: ["workflows"],
|
|
1166
|
+
group: "Your projects",
|
|
1167
|
+
summary: "the automations a project has",
|
|
1168
|
+
usage: "actions [project]",
|
|
1169
|
+
detail: "Shows what each action runs, what it runs on, and how many of its\n" +
|
|
1170
|
+
"runs have passed. Use `coderook runner` to execute them on this machine.",
|
|
1171
|
+
run: service_commands_js_1.commandWorkflows,
|
|
1172
|
+
},
|
|
1173
|
+
{
|
|
1174
|
+
name: "delete",
|
|
1175
|
+
group: "Your projects",
|
|
1176
|
+
summary: "delete a project",
|
|
1177
|
+
usage: "delete [project]",
|
|
1178
|
+
detail: "Asks for the project name to be typed before it does anything. The\n" +
|
|
1179
|
+
"service keeps deleted projects for 30 days before reclaiming the\n" +
|
|
1180
|
+
"storage, so a mistake made today is recoverable this month.",
|
|
1181
|
+
options: [{ flags: "--yes", description: "skip the confirmation" }],
|
|
1182
|
+
run: service_commands_js_1.commandDelete,
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
name: "people",
|
|
1186
|
+
aliases: ["collaborators"],
|
|
1187
|
+
group: "People",
|
|
1188
|
+
summary: "who can reach a project, and invite somebody",
|
|
1189
|
+
usage: "people [project]",
|
|
1190
|
+
options: [
|
|
1191
|
+
{ flags: "--invite <email>", description: "ask somebody to join" },
|
|
1192
|
+
{ flags: "--role <role>", description: "what they may do (default member)" },
|
|
1193
|
+
],
|
|
1194
|
+
examples: ["coderook people my-project --invite sam@example.com"],
|
|
1195
|
+
run: service_commands_js_1.commandCollaborators,
|
|
1196
|
+
},
|
|
1197
|
+
{
|
|
1198
|
+
name: "watch",
|
|
1199
|
+
group: "People",
|
|
1200
|
+
summary: "what reaches you about a project",
|
|
1201
|
+
usage: "watch [project] [--level versions]",
|
|
1202
|
+
options: [
|
|
1203
|
+
{
|
|
1204
|
+
flags: "--level <level>",
|
|
1205
|
+
description: "everything, versions, issues, releases, ignore",
|
|
1206
|
+
},
|
|
1207
|
+
{ flags: "--email [off]", description: "include it in the daily email" },
|
|
1208
|
+
],
|
|
1209
|
+
run: service_commands_js_1.commandWatch,
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
name: "tokens",
|
|
1213
|
+
group: "Other",
|
|
1214
|
+
summary: "the tokens that can act as your account",
|
|
1215
|
+
usage: "tokens",
|
|
1216
|
+
detail: "Lists personal access tokens with when each was last used, which is\n" +
|
|
1217
|
+
"how you find one that is no longer needed. Create new ones on the\n" +
|
|
1218
|
+
"website; they are only shown once.",
|
|
1219
|
+
options: [{ flags: "--revoke <id>", description: "stop one working now" }],
|
|
1220
|
+
run: service_commands_js_1.commandTokens,
|
|
1221
|
+
},
|
|
1222
|
+
{
|
|
1223
|
+
name: "merges",
|
|
1224
|
+
group: "When somebody saved first",
|
|
1225
|
+
summary: "uploads of yours waiting on a decision",
|
|
1226
|
+
usage: "merges",
|
|
1227
|
+
run: commandMerges,
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
name: "merge",
|
|
1231
|
+
group: "When somebody saved first",
|
|
1232
|
+
summary: "look at one, and decide",
|
|
1233
|
+
usage: "merge <ref>",
|
|
1234
|
+
options: [
|
|
1235
|
+
{ flags: "--mine", description: "keep your side" },
|
|
1236
|
+
{ flags: "--theirs", description: "keep theirs" },
|
|
1237
|
+
{ flags: "--both", description: "keep both" },
|
|
1238
|
+
{ flags: "--drop", description: "abandon the upload" },
|
|
1239
|
+
],
|
|
1240
|
+
run: commandMerge,
|
|
1241
|
+
},
|
|
1242
|
+
{
|
|
1243
|
+
name: "bundle",
|
|
1244
|
+
group: "Bundles",
|
|
1245
|
+
summary: "pack the project as a .cbx",
|
|
1246
|
+
usage: "bundle [folder] [out]",
|
|
1247
|
+
run: commandBundle,
|
|
1248
|
+
},
|
|
1249
|
+
{
|
|
1250
|
+
name: "unbundle",
|
|
1251
|
+
group: "Bundles",
|
|
1252
|
+
summary: "extract a .cbx",
|
|
1253
|
+
usage: "unbundle <file> [dir]",
|
|
1254
|
+
run: commandUnbundle,
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
name: "inspect",
|
|
1258
|
+
group: "Bundles",
|
|
1259
|
+
summary: "what a .cbx contains",
|
|
1260
|
+
usage: "inspect <file>",
|
|
1261
|
+
options: [{ flags: "--files", description: "list every file inside" }],
|
|
1262
|
+
run: commandInspect,
|
|
1263
|
+
},
|
|
1264
|
+
{
|
|
1265
|
+
name: "runner",
|
|
1266
|
+
group: "Actions",
|
|
1267
|
+
summary: "take this project's runs and do them here",
|
|
1268
|
+
usage: "runner <project>",
|
|
1269
|
+
detail: "Claims queued runs for a project and executes them on this machine.\n" +
|
|
1270
|
+
"Credentials in your environment are not passed to the commands it runs.",
|
|
1271
|
+
options: [
|
|
1272
|
+
{ flags: "--once", description: "do one job and stop" },
|
|
1273
|
+
{ flags: "--name <label>", description: "how this machine is listed" },
|
|
1274
|
+
{ flags: "--labels <a,b>", description: "what kinds of run it answers to" },
|
|
1275
|
+
{ flags: "--poll <seconds>", description: "how long between asks" },
|
|
1276
|
+
],
|
|
1277
|
+
examples: ["coderook runner my-game --labels windows,signing"],
|
|
1278
|
+
run: commandRunner,
|
|
1279
|
+
},
|
|
1280
|
+
{
|
|
1281
|
+
name: "doctor",
|
|
1282
|
+
group: "Other",
|
|
1283
|
+
summary: "check the service, config and sign-in",
|
|
1284
|
+
usage: "doctor",
|
|
1285
|
+
run: () => commandDoctor(),
|
|
1286
|
+
},
|
|
1287
|
+
];
|
|
1288
|
+
const REGISTRY = (0, registry_js_1.buildRegistry)(SPECS);
|
|
947
1289
|
async function main(argv) {
|
|
948
1290
|
const [name, ...rest] = argv;
|
|
949
|
-
if (!name || name === "
|
|
950
|
-
console.log(
|
|
1291
|
+
if (!name || name === "--help" || name === "-h") {
|
|
1292
|
+
console.log((0, help_js_1.renderHelp)(REGISTRY, VERSION));
|
|
951
1293
|
return 0;
|
|
952
1294
|
}
|
|
953
1295
|
if (name === "--version" || name === "-v" || name === "version") {
|
|
954
1296
|
console.log(VERSION);
|
|
955
1297
|
return 0;
|
|
956
1298
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1299
|
+
/*
|
|
1300
|
+
`coderook help submit` and `coderook submit --help` reach the same page.
|
|
1301
|
+
People reach for both, and one of them silently doing something else is
|
|
1302
|
+
the kind of small betrayal that makes a tool feel unreliable.
|
|
1303
|
+
*/
|
|
1304
|
+
if (name === "help") {
|
|
1305
|
+
const wanted = rest[0];
|
|
1306
|
+
if (!wanted) {
|
|
1307
|
+
console.log((0, help_js_1.renderHelp)(REGISTRY, VERSION));
|
|
1308
|
+
return 0;
|
|
1309
|
+
}
|
|
1310
|
+
const spec = REGISTRY.lookup.get(wanted);
|
|
1311
|
+
if (!spec) {
|
|
1312
|
+
console.error((0, help_js_1.renderUnknown)(wanted, (0, registry_js_1.nearestCommand)(REGISTRY, wanted)));
|
|
1313
|
+
return 1;
|
|
1314
|
+
}
|
|
1315
|
+
console.log((0, help_js_1.renderCommandHelp)(spec));
|
|
1316
|
+
return 0;
|
|
1317
|
+
}
|
|
1318
|
+
const spec = REGISTRY.lookup.get(name);
|
|
1319
|
+
if (!spec) {
|
|
1320
|
+
console.error((0, help_js_1.renderUnknown)(name, (0, registry_js_1.nearestCommand)(REGISTRY, name)));
|
|
961
1321
|
return 1;
|
|
962
1322
|
}
|
|
963
|
-
|
|
1323
|
+
if (rest.includes("--help") || rest.includes("-h")) {
|
|
1324
|
+
console.log((0, help_js_1.renderCommandHelp)(spec));
|
|
1325
|
+
return 0;
|
|
1326
|
+
}
|
|
1327
|
+
if (spec.deprecatedBy) {
|
|
1328
|
+
console.error(dim(`"${name}" is now "${spec.deprecatedBy}". The old name still works.`));
|
|
1329
|
+
}
|
|
1330
|
+
return spec.run(parse(rest));
|
|
964
1331
|
}
|
|
965
1332
|
/**
|
|
966
1333
|
* Set the status and let Node wind down on its own.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* What `coderook help` prints.
|
|
4
|
+
*
|
|
5
|
+
* Generated from the registry rather than written out, so a command that
|
|
6
|
+
* exists is listed and a command that is listed exists. The previous usage
|
|
7
|
+
* text was maintained by hand next to the dispatch table, which is the
|
|
8
|
+
* arrangement where the two drift and nobody finds out until somebody follows
|
|
9
|
+
* the help and it does not work.
|
|
10
|
+
*
|
|
11
|
+
* Two levels, because they answer different questions. The list answers "what
|
|
12
|
+
* can this thing do", and is scanned rather than read — so it is one line per
|
|
13
|
+
* command, aligned, grouped by what somebody is trying to achieve. The detail
|
|
14
|
+
* answers "how exactly do I use this one", and is read properly, so it can
|
|
15
|
+
* afford whole sentences and examples.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.renderHelp = renderHelp;
|
|
19
|
+
exports.renderCommandHelp = renderCommandHelp;
|
|
20
|
+
exports.renderUnknown = renderUnknown;
|
|
21
|
+
const registry_js_1 = require("./registry.js");
|
|
22
|
+
const bold = (value) => `[1m${value}[0m`;
|
|
23
|
+
const dim = (value) => `[2m${value}[0m`;
|
|
24
|
+
const accent = (value) => `[33m${value}[0m`;
|
|
25
|
+
/** Longest visible name, so the summary column lines up across every group. */
|
|
26
|
+
function nameColumn(specs) {
|
|
27
|
+
return specs.reduce((widest, spec) => Math.max(widest, spec.usage.length), 0);
|
|
28
|
+
}
|
|
29
|
+
function renderHelp(registry, version) {
|
|
30
|
+
const listed = registry.specs.filter((spec) => !spec.deprecatedBy);
|
|
31
|
+
const column = Math.min(nameColumn(listed), 34);
|
|
32
|
+
const lines = [
|
|
33
|
+
`${bold("coderook")} ${dim(version)} — CodeRook from the command line`,
|
|
34
|
+
"",
|
|
35
|
+
];
|
|
36
|
+
for (const group of registry_js_1.GROUP_ORDER) {
|
|
37
|
+
const inGroup = listed.filter((spec) => spec.group === group);
|
|
38
|
+
if (!inGroup.length)
|
|
39
|
+
continue;
|
|
40
|
+
lines.push(bold(group));
|
|
41
|
+
for (const spec of inGroup) {
|
|
42
|
+
lines.push(` ${spec.usage.padEnd(column)} ${spec.summary}`);
|
|
43
|
+
}
|
|
44
|
+
lines.push("");
|
|
45
|
+
}
|
|
46
|
+
lines.push(dim("coderook help <command> what one command does, in full"), dim("CODEROOK_TOKEN is used when set, so automated runs need nothing on disk."), dim("CODEROOK_API_URL points at another service."));
|
|
47
|
+
return lines.join("\n");
|
|
48
|
+
}
|
|
49
|
+
function renderCommandHelp(spec) {
|
|
50
|
+
const lines = [
|
|
51
|
+
`${bold("coderook " + spec.name)} — ${spec.summary}`,
|
|
52
|
+
"",
|
|
53
|
+
bold("Usage"),
|
|
54
|
+
` coderook ${spec.usage}`,
|
|
55
|
+
];
|
|
56
|
+
if (spec.aliases?.length) {
|
|
57
|
+
lines.push("", bold("Also"), ` ${spec.aliases.map((alias) => `coderook ${alias}`).join(" ")}`);
|
|
58
|
+
}
|
|
59
|
+
if (spec.detail) {
|
|
60
|
+
lines.push("", spec.detail.trim());
|
|
61
|
+
}
|
|
62
|
+
if (spec.options?.length) {
|
|
63
|
+
const width = spec.options.reduce((widest, option) => Math.max(widest, option.flags.length), 0);
|
|
64
|
+
lines.push("", bold("Options"));
|
|
65
|
+
for (const option of spec.options) {
|
|
66
|
+
lines.push(` ${option.flags.padEnd(width)} ${option.description}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (spec.examples?.length) {
|
|
70
|
+
lines.push("", bold("Examples"));
|
|
71
|
+
for (const example of spec.examples)
|
|
72
|
+
lines.push(` ${accent(example)}`);
|
|
73
|
+
}
|
|
74
|
+
return lines.join("\n");
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* What to print when somebody asks for help on something that is not a command.
|
|
78
|
+
*
|
|
79
|
+
* Suggests a near match where there is one, and otherwise points at the list —
|
|
80
|
+
* a confident wrong suggestion sends somebody off to read the wrong page.
|
|
81
|
+
*/
|
|
82
|
+
function renderUnknown(typed, suggestion) {
|
|
83
|
+
const lines = [`Unknown command: ${typed}`];
|
|
84
|
+
if (suggestion)
|
|
85
|
+
lines.push(`Did you mean ${accent("coderook " + suggestion)}?`);
|
|
86
|
+
lines.push(dim("Run coderook help to see everything."));
|
|
87
|
+
return lines.join("\n");
|
|
88
|
+
}
|