@skillcdn/console 0.3.1 → 0.4.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 +4 -4
- package/dist/api.d.ts +4 -4
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +4 -4
- package/dist/api.js.map +1 -1
- package/dist/cli/cli.d.ts +4 -0
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +201 -25
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +15 -11
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +59 -26
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/main.js +1 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/pack.d.ts +7 -2
- package/dist/cli/pack.d.ts.map +1 -1
- package/dist/cli/pack.js +16 -2
- package/dist/cli/pack.js.map +1 -1
- package/dist/client.d.ts +20 -10
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +17 -3
- package/dist/client.js.map +1 -1
- package/dist/compat.d.ts +28 -6
- package/dist/compat.d.ts.map +1 -1
- package/dist/compat.js +55 -14
- package/dist/compat.js.map +1 -1
- package/dist/components/board.d.ts +12 -1
- package/dist/components/board.d.ts.map +1 -1
- package/dist/components/board.js +57 -7
- package/dist/components/board.js.map +1 -1
- package/dist/components/calendar.d.ts +17 -7
- package/dist/components/calendar.d.ts.map +1 -1
- package/dist/components/calendar.js +89 -38
- package/dist/components/calendar.js.map +1 -1
- package/dist/components/decision-form.d.ts +12 -4
- package/dist/components/decision-form.d.ts.map +1 -1
- package/dist/components/decision-form.js +24 -6
- package/dist/components/decision-form.js.map +1 -1
- package/dist/components/decision-list.d.ts +4 -0
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +9 -6
- package/dist/components/decision-list.js.map +1 -1
- package/dist/components/documents.d.ts.map +1 -1
- package/dist/components/documents.js +3 -3
- package/dist/components/documents.js.map +1 -1
- package/dist/components/event-feed.d.ts.map +1 -1
- package/dist/components/event-feed.js +63 -8
- package/dist/components/event-feed.js.map +1 -1
- package/dist/components/my-decisions.d.ts +20 -0
- package/dist/components/my-decisions.d.ts.map +1 -0
- package/dist/components/my-decisions.js +36 -0
- package/dist/components/my-decisions.js.map +1 -0
- package/dist/components/people.d.ts.map +1 -1
- package/dist/components/people.js +1 -1
- package/dist/components/people.js.map +1 -1
- package/dist/components/picker.d.ts +39 -0
- package/dist/components/picker.d.ts.map +1 -0
- package/dist/components/picker.js +155 -0
- package/dist/components/picker.js.map +1 -0
- package/dist/components/projects.d.ts.map +1 -1
- package/dist/components/projects.js +3 -3
- package/dist/components/projects.js.map +1 -1
- package/dist/components/roadmap.js +1 -1
- package/dist/components/roadmap.js.map +1 -1
- package/dist/components/runs.d.ts.map +1 -1
- package/dist/components/runs.js +1 -1
- package/dist/components/runs.js.map +1 -1
- package/dist/components/shell.d.ts.map +1 -1
- package/dist/components/shell.js +36 -1
- package/dist/components/shell.js.map +1 -1
- package/dist/components/task-form.d.ts +18 -4
- package/dist/components/task-form.d.ts.map +1 -1
- package/dist/components/task-form.js +56 -13
- package/dist/components/task-form.js.map +1 -1
- package/dist/components/task-list.js +2 -2
- package/dist/components/task-list.js.map +1 -1
- package/dist/components/task-view.d.ts +7 -1
- package/dist/components/task-view.d.ts.map +1 -1
- package/dist/components/task-view.js +7 -6
- package/dist/components/task-view.js.map +1 -1
- package/dist/components/ui.d.ts +17 -0
- package/dist/components/ui.d.ts.map +1 -1
- package/dist/components/ui.js +26 -11
- package/dist/components/ui.js.map +1 -1
- package/dist/components/work.d.ts.map +1 -1
- package/dist/components/work.js +5 -3
- package/dist/components/work.js.map +1 -1
- package/dist/console.css +1794 -433
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +93 -10
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts +21 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +24 -1
- package/dist/data.js.map +1 -1
- package/dist/i18n/en.d.ts +45 -3
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +52 -10
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +54 -12
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +2 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +2 -0
- package/dist/limits.js.map +1 -1
- package/dist/router.d.ts +9 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +12 -0
- package/dist/router.js.map +1 -1
- package/dist/routes.d.ts +3 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +3 -1
- package/dist/routes.js.map +1 -1
- package/dist/rules.js +1 -1
- package/dist/rules.js.map +1 -1
- package/dist/schemas.d.ts +252 -12
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +27 -10
- package/dist/schemas.js.map +1 -1
- package/dist/vocabulary.d.ts +2 -0
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +2 -0
- package/dist/vocabulary.js.map +1 -1
- package/docs/specs/cli.md +14 -13
- package/docs/specs/rest.md +24 -19
- package/package.json +2 -2
- package/skills/building-a-console/SKILL.md +4 -4
- package/skills/building-a-console/references/the-default-console.md +4 -4
- package/skills/building-a-console/references/the-look-from-a-website.md +5 -5
- package/skills/deploying-a-console/SKILL.md +2 -2
- package/skills/deploying-a-console/references/resources.md +1 -1
- package/skills/working-the-board/SKILL.md +8 -6
- package/skills/working-the-board/references/terms.md +3 -3
- package/src/api.ts +7 -0
- package/src/cli/cli.ts +254 -25
- package/src/cli/format.ts +22 -11
- package/src/cli/help.ts +63 -26
- package/src/cli/main.ts +1 -0
- package/src/cli/pack.ts +17 -1
- package/src/client.ts +44 -11
- package/src/compat.ts +63 -14
- package/src/components/board.tsx +124 -25
- package/src/components/calendar.tsx +258 -117
- package/src/components/decision-form.tsx +71 -23
- package/src/components/decision-list.tsx +30 -3
- package/src/components/documents.tsx +27 -11
- package/src/components/event-feed.tsx +116 -33
- package/src/components/my-decisions.tsx +138 -0
- package/src/components/people.tsx +3 -1
- package/src/components/picker.tsx +297 -0
- package/src/components/projects.tsx +7 -5
- package/src/components/roadmap.tsx +1 -1
- package/src/components/runs.tsx +5 -1
- package/src/components/shell.tsx +36 -1
- package/src/components/task-form.tsx +149 -63
- package/src/components/task-list.tsx +3 -3
- package/src/components/task-view.tsx +71 -56
- package/src/components/ui.tsx +89 -11
- package/src/components/work.tsx +6 -4
- package/src/console.tsx +157 -29
- package/src/data.ts +61 -1
- package/src/i18n/en.ts +54 -10
- package/src/i18n/ko.ts +56 -12
- package/src/index.ts +19 -1
- package/src/limits.ts +2 -0
- package/src/router.ts +18 -1
- package/src/routes.ts +3 -1
- package/src/rules.ts +1 -1
- package/src/schemas.ts +29 -9
- package/src/styles/console.css +1794 -433
- package/src/vocabulary.ts +3 -0
package/dist/cli/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { basename, extname } from "node:path";
|
|
2
2
|
import { parseArgs } from "node:util";
|
|
3
3
|
import { ApiError, createClient, } from "../client.js";
|
|
4
|
-
import { CLIENT_RANGE_HEADER, judgeClient, parseVersion, VERSION_HEADER } from "../compat.js";
|
|
5
|
-
import { addDays, dayOf as dayOfDate, isDay } from "../dates.js";
|
|
4
|
+
import { CLIENT_RANGE_HEADER, CONSOLE_AT_LEAST, consoleRange, formatRange, judgeClient, judgeConsole, parseVersion, VERSION_HEADER, formatVersion as versionText, } from "../compat.js";
|
|
5
|
+
import { addDays, dayOf as dayOfDate, firstDayOfWeek, isDay, weekOf } from "../dates.js";
|
|
6
6
|
import { DEFAULT_PAGE_LIMIT, LIST_LIMIT, MAX_OPTIONS, MIN_OPTIONS } from "../limits.js";
|
|
7
7
|
import { fillRules, RULES_PATH, RULES_TITLE } from "../rules.js";
|
|
8
8
|
import { hostOfLogin, } from "../schemas.js";
|
|
@@ -606,10 +606,13 @@ const whoami = async (args, io) => {
|
|
|
606
606
|
const verdict = command === undefined || server === undefined
|
|
607
607
|
? undefined
|
|
608
608
|
: judgeClient(command, server).verdict;
|
|
609
|
+
// The oldest console this command works with (ADR-0038), as it stands within the command's line.
|
|
610
|
+
const atLeast = io.consoleAtLeast ?? CONSOLE_AT_LEAST;
|
|
611
|
+
const floor = command === undefined ? atLeast : versionText(consoleRange(command, atLeast).atLeast);
|
|
609
612
|
// A console from before the rule says nothing of itself; the command says so instead.
|
|
610
613
|
const consoleWords = me.console === undefined
|
|
611
614
|
? "the console does not say its version, so it is older than 0.1.18"
|
|
612
|
-
: `the console is ${me.console.version} and takes ${me.console.clientRange}`;
|
|
615
|
+
: `the console is ${me.console.version} and takes ${me.console.clientRange}; this command works with a console from ${floor} on`;
|
|
613
616
|
answer(io, session, {
|
|
614
617
|
...me,
|
|
615
618
|
console: me.console ?? null,
|
|
@@ -673,6 +676,15 @@ const use = async (args, io) => {
|
|
|
673
676
|
].join("\n"));
|
|
674
677
|
return EXIT.ok;
|
|
675
678
|
};
|
|
679
|
+
/**
|
|
680
|
+
* The last day of this week (ADR-0039): the week begins as the workspace says, or, left to the
|
|
681
|
+
* language, as the person's language has it, else this machine's.
|
|
682
|
+
*/
|
|
683
|
+
async function endOfWeek(io, session, today) {
|
|
684
|
+
const me = await session.client.me();
|
|
685
|
+
const language = me.language ?? io.locale ?? "en";
|
|
686
|
+
return weekOf(today, firstDayOfWeek(language, me.workspace.weekStart ?? "auto")).at(-1) ?? today;
|
|
687
|
+
}
|
|
676
688
|
const tasks = async (args, io) => {
|
|
677
689
|
const { values } = parse(args, {
|
|
678
690
|
state: { type: "string" },
|
|
@@ -682,12 +694,13 @@ const tasks = async (args, io) => {
|
|
|
682
694
|
category: { type: "string" },
|
|
683
695
|
goal: { type: "string" },
|
|
684
696
|
due: { type: "string" },
|
|
697
|
+
search: { type: "string" },
|
|
685
698
|
archived: { type: "boolean" },
|
|
686
699
|
...PAGE_OPTIONS,
|
|
687
700
|
}, false);
|
|
688
701
|
const state = stateOf(text(values, "state"));
|
|
689
702
|
if (on(values, "mine") && on(values, "unassigned")) {
|
|
690
|
-
throw misuse("Say one: --mine for the tasks
|
|
703
|
+
throw misuse("Say one: --mine for the tasks you work on, or --unassigned for those nobody works on");
|
|
691
704
|
}
|
|
692
705
|
const page = pageOf(values);
|
|
693
706
|
const session = await open(io, values);
|
|
@@ -700,19 +713,22 @@ const tasks = async (args, io) => {
|
|
|
700
713
|
: due === "overdue"
|
|
701
714
|
? addDays(today, -1)
|
|
702
715
|
: due === "week"
|
|
703
|
-
?
|
|
716
|
+
? await endOfWeek(io, session, today)
|
|
704
717
|
: (dayOf(io, due) ?? undefined);
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
718
|
+
const worker = on(values, "mine") ? ((await personIdOf(session, "me")) ?? undefined) : undefined;
|
|
719
|
+
const search = text(values, "search")?.trim();
|
|
720
|
+
if (search === "") {
|
|
721
|
+
throw misuse("Say what to search for: console tasks --search <words>");
|
|
722
|
+
}
|
|
708
723
|
const found = await project.tasks({
|
|
709
724
|
...(state === undefined ? {} : { state }),
|
|
710
725
|
...(on(values, "open") || due === "overdue" ? { open: true } : {}),
|
|
711
|
-
...(
|
|
726
|
+
...(worker === undefined ? {} : { worker }),
|
|
712
727
|
...(on(values, "unassigned") ? { unassigned: true } : {}),
|
|
713
728
|
...(text(values, "category") === undefined ? {} : { category: text(values, "category") }),
|
|
714
729
|
...(text(values, "goal") === undefined ? {} : { goal: text(values, "goal") }),
|
|
715
730
|
...(dueBy === undefined ? {} : { dueBy }),
|
|
731
|
+
...(search === undefined ? {} : { q: search }),
|
|
716
732
|
...(on(values, "archived") ? { archived: true } : {}),
|
|
717
733
|
...page,
|
|
718
734
|
});
|
|
@@ -813,14 +829,41 @@ const skills = async (args, io) => {
|
|
|
813
829
|
answer(io, session, { ...browsed, read: whole === undefined ? null : bodyOfFolder(whole) }, () => formatSkillBrowse(browsed, whole));
|
|
814
830
|
return EXIT.ok;
|
|
815
831
|
};
|
|
832
|
+
/** The most characters one read of a file asks for: what the console's API allows on one page. */
|
|
833
|
+
const MAX_FILE_LIMIT = 100_000;
|
|
834
|
+
/** A count given as a flag, within its bounds, or nothing; anything else is not understood. */
|
|
835
|
+
function countOf(given, name, bounds) {
|
|
836
|
+
if (given === undefined) {
|
|
837
|
+
return undefined;
|
|
838
|
+
}
|
|
839
|
+
const count = Number(given);
|
|
840
|
+
if (!Number.isInteger(count) ||
|
|
841
|
+
count < bounds.least ||
|
|
842
|
+
(bounds.most !== undefined && count > bounds.most)) {
|
|
843
|
+
const range = bounds.most === undefined ? `${bounds.least} or more` : `${bounds.least} to ${bounds.most}`;
|
|
844
|
+
throw misuse(`Not a number for --${name}, ${range}: ${given}`);
|
|
845
|
+
}
|
|
846
|
+
return count;
|
|
847
|
+
}
|
|
816
848
|
const skill = async (args, io) => {
|
|
817
|
-
const { values, positionals } = parse(args, {
|
|
849
|
+
const { values, positionals } = parse(args, {
|
|
850
|
+
file: { type: "string" },
|
|
851
|
+
offset: { type: "string" },
|
|
852
|
+
limit: { type: "string" },
|
|
853
|
+
});
|
|
818
854
|
const file = text(values, "file");
|
|
819
855
|
// A file is read by its own path from the repository's root; a skill's path beside it would be
|
|
820
856
|
// ignored, which reads as if it mattered, so the two together are not understood.
|
|
821
857
|
if (file !== undefined && positionals.length > 0) {
|
|
822
858
|
throw misuse("Say one: console skill <path of its SKILL.md> for a skill, or console skill --file <path> for a file of the pack.");
|
|
823
859
|
}
|
|
860
|
+
// A section is a page of a file read as asked (SkillCDN 0.4): the flags mean nothing without one.
|
|
861
|
+
if (file === undefined &&
|
|
862
|
+
(text(values, "offset") !== undefined || text(values, "limit") !== undefined)) {
|
|
863
|
+
throw misuse("--offset and --limit go with --file: console skill --file <path> [--offset <n>] [--limit <n>]");
|
|
864
|
+
}
|
|
865
|
+
const offset = countOf(text(values, "offset"), "offset", { least: 0 });
|
|
866
|
+
const limit = countOf(text(values, "limit"), "limit", { least: 1, most: MAX_FILE_LIMIT });
|
|
824
867
|
const session = await open(io, values);
|
|
825
868
|
const project = await session.project();
|
|
826
869
|
const { listing, summary } = await packOf(project);
|
|
@@ -828,6 +871,14 @@ const skill = async (args, io) => {
|
|
|
828
871
|
answer(io, session, listing, () => formatSkills(listing, io.skillsDir));
|
|
829
872
|
return EXIT.ok;
|
|
830
873
|
}
|
|
874
|
+
if (file !== undefined && (offset !== undefined || limit !== undefined)) {
|
|
875
|
+
// One page as asked, the section a search or an outline named; the result says where the
|
|
876
|
+
// file continues, and asks for nothing.
|
|
877
|
+
const page = await project.skillFile(file, { offset, limit });
|
|
878
|
+
const section = page.read === null ? undefined : joinFile([page], summary, limit);
|
|
879
|
+
answer(io, session, { ...page, read: section === undefined ? null : bodyOfFile(section) }, () => formatSkillFile(page, section));
|
|
880
|
+
return EXIT.ok;
|
|
881
|
+
}
|
|
831
882
|
if (file !== undefined) {
|
|
832
883
|
// The file whole, page after page, as the console reads it from the pack.
|
|
833
884
|
const pages = [];
|
|
@@ -980,9 +1031,37 @@ const TASK_FIELDS = {
|
|
|
980
1031
|
goal: { type: "string" },
|
|
981
1032
|
starts: { type: "string" },
|
|
982
1033
|
due: { type: "string" },
|
|
983
|
-
|
|
1034
|
+
worker: { type: "string", multiple: true },
|
|
984
1035
|
parent: { type: "string" },
|
|
985
1036
|
};
|
|
1037
|
+
/**
|
|
1038
|
+
* The people `--worker` names, each a login or `me`, given again or with commas between for
|
|
1039
|
+
* several, in place of those there were; `none` alone for nobody; nothing when the flag was
|
|
1040
|
+
* not given.
|
|
1041
|
+
*/
|
|
1042
|
+
async function workerIdsOf(session, given) {
|
|
1043
|
+
if (given.length === 0) {
|
|
1044
|
+
return undefined;
|
|
1045
|
+
}
|
|
1046
|
+
const names = given
|
|
1047
|
+
.flatMap((value) => value.split(","))
|
|
1048
|
+
.map((name) => name.trim())
|
|
1049
|
+
.filter((name) => name.length > 0);
|
|
1050
|
+
if (names.includes("none")) {
|
|
1051
|
+
if (names.length > 1) {
|
|
1052
|
+
throw misuse("Say --worker none alone, for nobody; or the logins of the workers.");
|
|
1053
|
+
}
|
|
1054
|
+
return [];
|
|
1055
|
+
}
|
|
1056
|
+
const ids = [];
|
|
1057
|
+
for (const name of names) {
|
|
1058
|
+
const id = await personIdOf(session, name);
|
|
1059
|
+
if (typeof id === "string" && !ids.includes(id)) {
|
|
1060
|
+
ids.push(id);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return ids;
|
|
1064
|
+
}
|
|
986
1065
|
/** What the flags say of a task's fields, as the API takes them; `none` clears a field. */
|
|
987
1066
|
async function taskFieldsOf(io, session, project, values) {
|
|
988
1067
|
const state = stateOf(text(values, "state"));
|
|
@@ -991,7 +1070,7 @@ async function taskFieldsOf(io, session, project, values) {
|
|
|
991
1070
|
const goalId = orNone(text(values, "goal"));
|
|
992
1071
|
const startsOn = dayOf(io, text(values, "starts"), true);
|
|
993
1072
|
const dueOn = dayOf(io, text(values, "due"), true);
|
|
994
|
-
const
|
|
1073
|
+
const workerIds = await workerIdsOf(session, texts(values, "worker"));
|
|
995
1074
|
const parent = text(values, "parent");
|
|
996
1075
|
const parentId = parent === undefined ? undefined : parent === "none" ? null : (await project.task(parent)).id;
|
|
997
1076
|
return {
|
|
@@ -1001,7 +1080,7 @@ async function taskFieldsOf(io, session, project, values) {
|
|
|
1001
1080
|
...(goalId === undefined ? {} : { goalId }),
|
|
1002
1081
|
...(startsOn === undefined ? {} : { startsOn }),
|
|
1003
1082
|
...(dueOn === undefined ? {} : { dueOn }),
|
|
1004
|
-
...(
|
|
1083
|
+
...(workerIds === undefined ? {} : { workerIds }),
|
|
1005
1084
|
...(parentId === undefined ? {} : { parentId }),
|
|
1006
1085
|
};
|
|
1007
1086
|
}
|
|
@@ -1042,7 +1121,7 @@ const editTask = async (args, io) => {
|
|
|
1042
1121
|
...(await taskFieldsOf(io, session, project, values)),
|
|
1043
1122
|
};
|
|
1044
1123
|
if (Object.keys(patch).length === 0) {
|
|
1045
|
-
throw misuse("Say what to change: --title, --body, --state, --priority, --category, --goal, --starts, --due, --
|
|
1124
|
+
throw misuse("Say what to change: --title, --body, --state, --priority, --category, --goal, --starts, --due, --worker or --parent; none clears a field.");
|
|
1046
1125
|
}
|
|
1047
1126
|
const found = await project.task(ref);
|
|
1048
1127
|
const changed = await project.updateTask(found.id, patch);
|
|
@@ -1308,8 +1387,13 @@ const ask = async (args, io) => {
|
|
|
1308
1387
|
file: { type: "string" },
|
|
1309
1388
|
run: { type: "string" },
|
|
1310
1389
|
wait: { type: "string" },
|
|
1390
|
+
to: { type: "string" },
|
|
1311
1391
|
});
|
|
1312
1392
|
const question = theOne(positionals, 'Say the question: console ask "<question>" --option "<label>" --option "<label>"');
|
|
1393
|
+
const to = text(values, "to");
|
|
1394
|
+
if (to === "none") {
|
|
1395
|
+
throw misuse("Say whom the decision is for: --to <login|me>.");
|
|
1396
|
+
}
|
|
1313
1397
|
const options = texts(values, "option");
|
|
1314
1398
|
if (options.length < MIN_OPTIONS || options.length > MAX_OPTIONS) {
|
|
1315
1399
|
throw misuse(`Give ${MIN_OPTIONS} to ${MAX_OPTIONS} options, one --option each.`);
|
|
@@ -1319,18 +1403,49 @@ const ask = async (args, io) => {
|
|
|
1319
1403
|
const session = await open(io, values);
|
|
1320
1404
|
const project = await session.project();
|
|
1321
1405
|
const runId = await currentRun(project, values);
|
|
1406
|
+
// Whom the decision is for (ADR-0042): the person the agent works for, unless --to names another.
|
|
1407
|
+
const deciderId = await personIdOf(session, to);
|
|
1322
1408
|
const raised = await project.raiseDecision({
|
|
1323
1409
|
question,
|
|
1324
1410
|
options: options.map((label) => ({ label })),
|
|
1325
1411
|
runId,
|
|
1326
1412
|
...(body === undefined ? {} : { body }),
|
|
1413
|
+
...(typeof deciderId === "string" ? { deciderId } : {}),
|
|
1327
1414
|
});
|
|
1328
1415
|
// The id is printed before any waiting, so that a wait cut short loses nothing.
|
|
1329
1416
|
io.stdout(session.json
|
|
1330
1417
|
? `${JSON.stringify(raised, null, 2)}\n`
|
|
1331
|
-
: `Decision ${raised.id} raised on run ${runId}; the run waits for
|
|
1418
|
+
: `Decision ${raised.id} raised on run ${runId}; the run waits for ${raised.decider.login}.\ndecision id: ${raised.id}\n${formatOptions(raised)}\n`);
|
|
1332
1419
|
return settle(io, session, project, raised, seconds, true);
|
|
1333
1420
|
};
|
|
1421
|
+
/**
|
|
1422
|
+
* Writes a person's answer on a decision, in their name through the agent (ADR-0021): the
|
|
1423
|
+
* option they chose, or their own words, and a note about it, so that a choice made in chat
|
|
1424
|
+
* or in passing reaches the record the run waits on.
|
|
1425
|
+
*/
|
|
1426
|
+
const answering = async (args, io) => {
|
|
1427
|
+
const { values, positionals } = parse(args, {
|
|
1428
|
+
option: { type: "string" },
|
|
1429
|
+
own: { type: "string" },
|
|
1430
|
+
note: { type: "string" },
|
|
1431
|
+
file: { type: "string" },
|
|
1432
|
+
});
|
|
1433
|
+
const id = theOne(positionals, 'Say which decision: console answer <id> --option <n>|--own "<answer>" [--note <markdown>|--file <path>]');
|
|
1434
|
+
const option = text(values, "option");
|
|
1435
|
+
const own = text(values, "own");
|
|
1436
|
+
if ((option === undefined) === (own === undefined)) {
|
|
1437
|
+
throw misuse('Say the option chosen, --option <n>, or the person\'s own answer, --own "<words>": one of the two.');
|
|
1438
|
+
}
|
|
1439
|
+
const note = await bodyOf(io, values, text(values, "note"));
|
|
1440
|
+
const session = await open(io, values);
|
|
1441
|
+
const project = await session.project();
|
|
1442
|
+
const answered = await project.answerDecision(id, {
|
|
1443
|
+
...(own === undefined ? { option } : { own }),
|
|
1444
|
+
...(note === undefined ? {} : { note }),
|
|
1445
|
+
});
|
|
1446
|
+
answer(io, session, answered, () => formatDecision(answered));
|
|
1447
|
+
return EXIT.ok;
|
|
1448
|
+
};
|
|
1334
1449
|
const decision = async (args, io) => {
|
|
1335
1450
|
const { values, positionals } = parse(args, {
|
|
1336
1451
|
wait: { type: "string" },
|
|
@@ -1487,20 +1602,33 @@ const restore = async (args, io) => {
|
|
|
1487
1602
|
return EXIT.ok;
|
|
1488
1603
|
};
|
|
1489
1604
|
const goals = async (args, io) => {
|
|
1490
|
-
const { values } = parse(args, {
|
|
1605
|
+
const { values } = parse(args, {
|
|
1606
|
+
category: { type: "string" },
|
|
1607
|
+
state: { type: "string" },
|
|
1608
|
+
search: { type: "string" },
|
|
1609
|
+
...PAGE_OPTIONS,
|
|
1610
|
+
}, false);
|
|
1491
1611
|
const state = goalStateOf(text(values, "state"));
|
|
1492
1612
|
const category = text(values, "category");
|
|
1613
|
+
const search = text(values, "search")?.trim();
|
|
1614
|
+
if (search === "") {
|
|
1615
|
+
throw misuse("Say what to search for: console goals --search <words>");
|
|
1616
|
+
}
|
|
1617
|
+
const page = pageOf(values);
|
|
1493
1618
|
const session = await open(io, values);
|
|
1494
1619
|
const project = await session.project();
|
|
1495
1620
|
const found = await project.goals({
|
|
1496
1621
|
...(state === undefined ? {} : { state }),
|
|
1497
1622
|
...(category === undefined ? {} : { category }),
|
|
1623
|
+
...(search === undefined ? {} : { q: search }),
|
|
1624
|
+
...page,
|
|
1498
1625
|
});
|
|
1499
1626
|
answer(io, session, found, () => found.items.length === 0
|
|
1500
|
-
? state === undefined && category === undefined
|
|
1627
|
+
? state === undefined && category === undefined && search === undefined
|
|
1501
1628
|
? "No goals. An owner sets one: console goal new <title> --due <day>"
|
|
1502
1629
|
: "No goal matches."
|
|
1503
|
-
: found.items.map(formatGoalLine).join("\n")
|
|
1630
|
+
: found.items.map(formatGoalLine).join("\n") +
|
|
1631
|
+
formatMore(found.more, page.limit, page.offset));
|
|
1504
1632
|
return EXIT.ok;
|
|
1505
1633
|
};
|
|
1506
1634
|
const GOAL_FIELDS = {
|
|
@@ -1579,14 +1707,31 @@ const goal = async (args, io) => {
|
|
|
1579
1707
|
return EXIT.ok;
|
|
1580
1708
|
};
|
|
1581
1709
|
const decisions = async (args, io) => {
|
|
1582
|
-
const { values } = parse(args, {
|
|
1710
|
+
const { values } = parse(args, {
|
|
1711
|
+
open: { type: "boolean" },
|
|
1712
|
+
mine: { type: "boolean" },
|
|
1713
|
+
raised: { type: "boolean" },
|
|
1714
|
+
...PAGE_OPTIONS,
|
|
1715
|
+
}, false);
|
|
1583
1716
|
const page = pageOf(values);
|
|
1584
1717
|
const session = await open(io, values);
|
|
1718
|
+
const open_ = on(values, "open") ? { open: true } : {};
|
|
1719
|
+
if (on(values, "mine") || on(values, "raised")) {
|
|
1720
|
+
// One's own across the projects (ADR-0042), addressed to the person or raised by them for
|
|
1721
|
+
// someone else: the waiting first, each with its project's key.
|
|
1722
|
+
const raised = on(values, "raised");
|
|
1723
|
+
const found = await session.client.myDecisions({ ...open_, raised, ...page });
|
|
1724
|
+
answer(io, session, found, () => found.items.length === 0
|
|
1725
|
+
? raised
|
|
1726
|
+
? "No decisions you asked of others."
|
|
1727
|
+
: "No decisions of yours."
|
|
1728
|
+
: found.items
|
|
1729
|
+
.map(({ project, decision }) => `${project.key} ${formatDecisionLine(decision)}`)
|
|
1730
|
+
.join("\n") + formatMore(found.more, page.limit, page.offset));
|
|
1731
|
+
return EXIT.ok;
|
|
1732
|
+
}
|
|
1585
1733
|
const project = await session.project();
|
|
1586
|
-
const found = await project.decisions({
|
|
1587
|
-
...(on(values, "open") ? { open: true } : {}),
|
|
1588
|
-
...page,
|
|
1589
|
-
});
|
|
1734
|
+
const found = await project.decisions({ ...open_, ...page });
|
|
1590
1735
|
answer(io, session, found, () => found.items.length === 0
|
|
1591
1736
|
? "No decisions."
|
|
1592
1737
|
: found.items.map(formatDecisionLine).join("\n") +
|
|
@@ -2155,6 +2300,7 @@ const COMMANDS = {
|
|
|
2155
2300
|
comment,
|
|
2156
2301
|
file,
|
|
2157
2302
|
ask,
|
|
2303
|
+
answer: answering,
|
|
2158
2304
|
decision,
|
|
2159
2305
|
decisions,
|
|
2160
2306
|
finish: ending("finished"),
|
|
@@ -2238,11 +2384,35 @@ async function noticeVersions(io, seen) {
|
|
|
2238
2384
|
shownAt: new Date(now).toISOString(),
|
|
2239
2385
|
});
|
|
2240
2386
|
}
|
|
2387
|
+
/**
|
|
2388
|
+
* The refusal of a console older than this command works with (ADR-0038), in a console's own
|
|
2389
|
+
* form: a `426` the command reads as it reads the console's, before it acts on anything the
|
|
2390
|
+
* console answered.
|
|
2391
|
+
*/
|
|
2392
|
+
function consoleRefusal(io, seen) {
|
|
2393
|
+
const command = parseVersion(io.version);
|
|
2394
|
+
const server = parseVersion(seen.version);
|
|
2395
|
+
if (command === undefined || server === undefined) {
|
|
2396
|
+
return undefined;
|
|
2397
|
+
}
|
|
2398
|
+
const { verdict, range } = judgeConsole(server, command, io.consoleAtLeast ?? CONSOLE_AT_LEAST);
|
|
2399
|
+
if (verdict !== "outdated") {
|
|
2400
|
+
return undefined;
|
|
2401
|
+
}
|
|
2402
|
+
return Response.json({
|
|
2403
|
+
error: {
|
|
2404
|
+
code: "console.outdated",
|
|
2405
|
+
message: `This console, ${seen.version}, is older than this command (${io.version}) works with (${formatRange(range)}).`,
|
|
2406
|
+
},
|
|
2407
|
+
}, { status: 426 });
|
|
2408
|
+
}
|
|
2241
2409
|
/** What a refusal of the console is followed by, when there is something to do about it. */
|
|
2242
2410
|
function hintFor(error, io, seen) {
|
|
2243
2411
|
switch (error.code) {
|
|
2244
2412
|
case "client.outdated":
|
|
2245
2413
|
return `Update the command: ${updateWords(io, seen.version)}\n`;
|
|
2414
|
+
case "console.outdated":
|
|
2415
|
+
return `Install the console's version: ${updateWords(io, seen.version)}; or ask the console's operator to update the console.\n`;
|
|
2246
2416
|
case "client.too_new":
|
|
2247
2417
|
return `Install the console's version: ${updateWords(io, seen.version)}; or ask the console's operator to update the console.\n`;
|
|
2248
2418
|
case "auth.required":
|
|
@@ -2317,7 +2487,11 @@ export async function runCli(argv, io) {
|
|
|
2317
2487
|
const response = await io.fetch(input, init);
|
|
2318
2488
|
seen.version = response.headers.get(VERSION_HEADER) ?? seen.version;
|
|
2319
2489
|
seen.range = response.headers.get(CLIENT_RANGE_HEADER) ?? seen.range;
|
|
2320
|
-
|
|
2490
|
+
// A console older than this command works with is refused here (ADR-0038); `serve`
|
|
2491
|
+
// carries the pages' own requests, which are not the command's to judge.
|
|
2492
|
+
// A refusal of the console's own is passed on as it is.
|
|
2493
|
+
return ((name === "serve" || response.status === 426 ? undefined : consoleRefusal(io, seen)) ??
|
|
2494
|
+
response);
|
|
2321
2495
|
},
|
|
2322
2496
|
};
|
|
2323
2497
|
try {
|
|
@@ -2348,7 +2522,9 @@ export async function runCli(argv, io) {
|
|
|
2348
2522
|
}
|
|
2349
2523
|
if (error instanceof ApiError) {
|
|
2350
2524
|
io.stderr(`${error.code}: ${error.message}\n${hintFor(error, io, seen)}`);
|
|
2351
|
-
return error.code === "client.outdated" ||
|
|
2525
|
+
return error.code === "client.outdated" ||
|
|
2526
|
+
error.code === "client.too_new" ||
|
|
2527
|
+
error.code === "console.outdated"
|
|
2352
2528
|
? EXIT.incompatible
|
|
2353
2529
|
: EXIT.failed;
|
|
2354
2530
|
}
|