@skillcdn/console 0.4.3 → 0.4.5
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 +6 -6
- 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.map +1 -1
- package/dist/cli/cli.js +255 -33
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts +20 -5
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +98 -26
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +39 -15
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/plugin-check.d.ts +5 -1
- package/dist/cli/plugin-check.d.ts.map +1 -1
- package/dist/cli/plugin-check.js +23 -4
- package/dist/cli/plugin-check.js.map +1 -1
- package/dist/cli/plugin-cli.d.ts.map +1 -1
- package/dist/cli/plugin-cli.js +10 -1
- package/dist/cli/plugin-cli.js.map +1 -1
- package/dist/cli/plugin-init.d.ts +2 -0
- package/dist/cli/plugin-init.d.ts.map +1 -1
- package/dist/cli/plugin-init.js +6 -0
- package/dist/cli/plugin-init.js.map +1 -1
- package/dist/client.d.ts +32 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +14 -2
- package/dist/client.js.map +1 -1
- package/dist/compat.js +1 -1
- package/dist/components/board.d.ts +8 -0
- package/dist/components/board.d.ts.map +1 -1
- package/dist/components/board.js +49 -18
- package/dist/components/board.js.map +1 -1
- package/dist/components/decision-form.d.ts +6 -3
- package/dist/components/decision-form.d.ts.map +1 -1
- package/dist/components/decision-form.js +24 -18
- package/dist/components/decision-form.js.map +1 -1
- package/dist/components/decision-list.d.ts +5 -1
- package/dist/components/decision-list.d.ts.map +1 -1
- package/dist/components/decision-list.js +25 -5
- package/dist/components/decision-list.js.map +1 -1
- package/dist/components/event-feed.d.ts.map +1 -1
- package/dist/components/event-feed.js +34 -8
- package/dist/components/event-feed.js.map +1 -1
- package/dist/components/goals.d.ts.map +1 -1
- package/dist/components/goals.js +2 -2
- package/dist/components/goals.js.map +1 -1
- package/dist/components/home.d.ts +35 -0
- package/dist/components/home.d.ts.map +1 -0
- package/dist/components/home.js +326 -0
- package/dist/components/home.js.map +1 -0
- package/dist/components/my-decisions.d.ts +20 -3
- package/dist/components/my-decisions.d.ts.map +1 -1
- package/dist/components/my-decisions.js +14 -1
- package/dist/components/my-decisions.js.map +1 -1
- package/dist/components/pack-connect.d.ts.map +1 -1
- package/dist/components/pack-connect.js +5 -4
- package/dist/components/pack-connect.js.map +1 -1
- package/dist/components/people.d.ts +45 -1
- package/dist/components/people.d.ts.map +1 -1
- package/dist/components/people.js +125 -4
- package/dist/components/people.js.map +1 -1
- package/dist/components/picker.d.ts +8 -0
- package/dist/components/picker.d.ts.map +1 -1
- package/dist/components/picker.js +2 -2
- package/dist/components/picker.js.map +1 -1
- package/dist/components/plugin-frame.d.ts +3 -13
- package/dist/components/plugin-frame.d.ts.map +1 -1
- package/dist/components/plugin-frame.js +75 -21
- package/dist/components/plugin-frame.js.map +1 -1
- package/dist/components/plugin-settings.d.ts +5 -1
- package/dist/components/plugin-settings.d.ts.map +1 -1
- package/dist/components/plugin-settings.js +283 -45
- package/dist/components/plugin-settings.js.map +1 -1
- package/dist/components/projects.d.ts +0 -8
- package/dist/components/projects.d.ts.map +1 -1
- package/dist/components/projects.js +4 -14
- package/dist/components/projects.js.map +1 -1
- package/dist/components/searches.d.ts +11 -0
- package/dist/components/searches.d.ts.map +1 -0
- package/dist/components/searches.js +30 -0
- package/dist/components/searches.js.map +1 -0
- package/dist/components/task-form.d.ts.map +1 -1
- package/dist/components/task-form.js +6 -5
- package/dist/components/task-form.js.map +1 -1
- package/dist/components/task-view.d.ts.map +1 -1
- package/dist/components/task-view.js +2 -1
- package/dist/components/task-view.js.map +1 -1
- package/dist/components/ui.d.ts +16 -0
- package/dist/components/ui.d.ts.map +1 -1
- package/dist/components/ui.js +15 -2
- package/dist/components/ui.js.map +1 -1
- package/dist/connect-page.d.ts.map +1 -1
- package/dist/connect-page.js +5 -6
- package/dist/connect-page.js.map +1 -1
- package/dist/console.css +1123 -258
- package/dist/console.d.ts +12 -4
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +123 -27
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts +18 -3
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +41 -1
- package/dist/data.js.map +1 -1
- package/dist/docs-page.d.ts.map +1 -1
- package/dist/docs-page.js +6 -5
- package/dist/docs-page.js.map +1 -1
- package/dist/home-tiles.d.ts +92 -0
- package/dist/home-tiles.d.ts.map +1 -0
- package/dist/home-tiles.js +294 -0
- package/dist/home-tiles.js.map +1 -0
- package/dist/i18n/en.d.ts +264 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +260 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/index.d.ts +4 -2
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +10 -11
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +255 -4
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +12 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +12 -0
- package/dist/limits.js.map +1 -1
- package/dist/plugin/args.d.ts +6 -1
- package/dist/plugin/args.d.ts.map +1 -1
- package/dist/plugin/args.js +16 -11
- package/dist/plugin/args.js.map +1 -1
- package/dist/plugin/bridge.d.ts +63 -3
- package/dist/plugin/bridge.d.ts.map +1 -1
- package/dist/plugin/bridge.js +54 -0
- package/dist/plugin/bridge.js.map +1 -1
- package/dist/plugin/capabilities.d.ts +7 -1
- package/dist/plugin/capabilities.d.ts.map +1 -1
- package/dist/plugin/capabilities.js +24 -1
- package/dist/plugin/capabilities.js.map +1 -1
- package/dist/plugin/context.d.ts +174 -12
- package/dist/plugin/context.d.ts.map +1 -1
- package/dist/plugin/define.d.ts +14 -1
- package/dist/plugin/define.d.ts.map +1 -1
- package/dist/plugin/define.js +18 -2
- package/dist/plugin/define.js.map +1 -1
- package/dist/plugin/limits.d.ts +9 -2
- package/dist/plugin/limits.d.ts.map +1 -1
- package/dist/plugin/limits.js +9 -2
- package/dist/plugin/limits.js.map +1 -1
- package/dist/plugin/manifest.d.ts +98 -6
- package/dist/plugin/manifest.d.ts.map +1 -1
- package/dist/plugin/manifest.js +171 -10
- package/dist/plugin/manifest.js.map +1 -1
- package/dist/plugin/rest-schemas.d.ts +120 -0
- package/dist/plugin/rest-schemas.d.ts.map +1 -1
- package/dist/plugin/rest-schemas.js +78 -5
- package/dist/plugin/rest-schemas.js.map +1 -1
- package/dist/plugin/sandbox.d.ts +4 -3
- package/dist/plugin/sandbox.d.ts.map +1 -1
- package/dist/plugin/sandbox.js +33 -6
- package/dist/plugin/sandbox.js.map +1 -1
- package/dist/plugin/semver.d.ts +26 -0
- package/dist/plugin/semver.d.ts.map +1 -0
- package/dist/plugin/semver.js +76 -0
- package/dist/plugin/semver.js.map +1 -0
- package/dist/plugin/testing.d.ts +15 -1
- package/dist/plugin/testing.d.ts.map +1 -1
- package/dist/plugin/testing.js +42 -5
- package/dist/plugin/testing.js.map +1 -1
- package/dist/plugin/web.d.ts +16 -1
- package/dist/plugin/web.d.ts.map +1 -1
- package/dist/plugin/web.js +44 -1
- package/dist/plugin/web.js.map +1 -1
- package/dist/plugin.d.ts +3 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +3 -2
- package/dist/plugin.js.map +1 -1
- package/dist/router.d.ts +19 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +18 -0
- package/dist/router.js.map +1 -1
- package/dist/routes.d.ts +11 -2
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +14 -1
- package/dist/routes.js.map +1 -1
- package/dist/rules.d.ts +2 -2
- package/dist/rules.js +3 -3
- package/dist/schemas.d.ts +864 -2
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +156 -9
- package/dist/schemas.js.map +1 -1
- package/dist/use-action.d.ts +8 -2
- package/dist/use-action.d.ts.map +1 -1
- package/dist/use-action.js +13 -6
- package/dist/use-action.js.map +1 -1
- package/dist/vocabulary.d.ts +3 -1
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +9 -0
- package/dist/vocabulary.js.map +1 -1
- package/docs/specs/cli.md +10 -6
- package/docs/specs/plugins.md +83 -62
- package/docs/specs/rest.md +34 -16
- package/package.json +1 -1
- package/skills/building-a-plugin/SKILL.md +15 -12
- package/skills/planning-the-board/references/setting-up.md +2 -1
- package/skills/working-the-board/SKILL.md +3 -3
- package/src/api.ts +23 -0
- package/src/cli/cli.ts +323 -30
- package/src/cli/format.ts +122 -26
- package/src/cli/help.ts +43 -14
- package/src/cli/plugin-check.ts +41 -2
- package/src/cli/plugin-cli.ts +10 -1
- package/src/cli/plugin-init.ts +8 -0
- package/src/client.ts +78 -0
- package/src/compat.ts +1 -1
- package/src/components/board.tsx +86 -45
- package/src/components/decision-form.tsx +73 -36
- package/src/components/decision-list.tsx +45 -13
- package/src/components/event-feed.tsx +40 -7
- package/src/components/goals.tsx +4 -1
- package/src/components/home.tsx +615 -0
- package/src/components/my-decisions.tsx +50 -5
- package/src/components/pack-connect.tsx +5 -4
- package/src/components/people.tsx +500 -61
- package/src/components/picker.tsx +11 -2
- package/src/components/plugin-frame.tsx +85 -63
- package/src/components/plugin-settings.tsx +735 -185
- package/src/components/projects.tsx +14 -69
- package/src/components/searches.ts +47 -0
- package/src/components/task-form.tsx +23 -28
- package/src/components/task-view.tsx +7 -0
- package/src/components/ui.tsx +42 -2
- package/src/connect-page.tsx +5 -6
- package/src/console.tsx +303 -64
- package/src/data.ts +62 -3
- package/src/docs-page.tsx +6 -5
- package/src/home-tiles.ts +438 -0
- package/src/i18n/en.ts +274 -0
- package/src/i18n/index.ts +17 -14
- package/src/i18n/ko.ts +267 -4
- package/src/index.ts +18 -5
- package/src/limits.ts +12 -0
- package/src/plugin/args.ts +16 -11
- package/src/plugin/bridge.ts +122 -1
- package/src/plugin/capabilities.ts +28 -1
- package/src/plugin/context.ts +186 -9
- package/src/plugin/define.ts +38 -1
- package/src/plugin/limits.ts +9 -2
- package/src/plugin/manifest.ts +228 -13
- package/src/plugin/rest-schemas.ts +89 -6
- package/src/plugin/sandbox.ts +36 -8
- package/src/plugin/semver.ts +89 -0
- package/src/plugin/testing.ts +63 -6
- package/src/plugin/web.ts +66 -0
- package/src/plugin.ts +19 -0
- package/src/router.ts +25 -0
- package/src/routes.ts +17 -2
- package/src/rules.ts +3 -3
- package/src/schemas.ts +197 -10
- package/src/styles/console.css +1123 -258
- package/src/use-action.ts +16 -7
- package/src/vocabulary.ts +10 -0
- package/template/plugin/console-plugin.json +3 -1
package/dist/cli/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { parseArgs } from "node:util";
|
|
|
3
3
|
import { ApiError, createClient, } from "../client.js";
|
|
4
4
|
import { CLIENT_RANGE_HEADER, CONSOLE_AT_LEAST, consoleRange, formatRange, judgeClient, judgeConsole, parseVersion, VERSION_HEADER, formatVersion as versionText, } from "../compat.js";
|
|
5
5
|
import { addDays, dayOf as dayOfDate, firstDayOfWeek, isDay, weekOf } from "../dates.js";
|
|
6
|
-
import { DEFAULT_PAGE_LIMIT, LIST_LIMIT, MAX_OPTIONS, MIN_OPTIONS } from "../limits.js";
|
|
6
|
+
import { DEFAULT_PAGE_LIMIT, LIST_LIMIT, MAX_DECIDERS, MAX_OPTIONS, MIN_OPTIONS, } from "../limits.js";
|
|
7
7
|
import { capabilityWords } from "../plugin/capabilities.js";
|
|
8
8
|
import { PLUGIN_LIST_LIMIT } from "../plugin/limits.js";
|
|
9
9
|
import { fillRules, RULES_PATH, RULES_TITLE } from "../rules.js";
|
|
@@ -12,7 +12,7 @@ import { COLOR_NAMES, GOAL_STATES, PEOPLE_VISIBILITIES, PERSON_ROLES, PROJECT_RO
|
|
|
12
12
|
import { loadWebRoot, WebRootError } from "../web.js";
|
|
13
13
|
import { CredentialsError, EMPTY_CREDENTIALS, loginsAt, resolveLogin, withLogin, withoutLogin, } from "./credentials.js";
|
|
14
14
|
import { THE_SENTENCE } from "./directory.js";
|
|
15
|
-
import { formatAnswer, formatCategoryLine, formatComment, formatDecision, formatDecisionLine, formatDocument, formatDocuments, formatGoal, formatGoalLine, formatInvocation, formatListingLine, formatMemberLine, formatMore, formatOptions, formatPersonLine, formatPersonWork, formatPlugin, formatPluginGrantLine, formatPluginHelp, formatPluginRecord, formatPluginRecords, formatPluginRecordVersions, formatPluginSettingLine, formatPlugins, formatProject, formatProjectLine, formatRules, formatRun, formatRunLine, formatTask, formatTaskLine, formatVersion, formatVersionLine, formatWork, formatWorkspace, localized, moment, setMomentZone, } from "./format.js";
|
|
15
|
+
import { deciderLogins, formatAnswer, formatCategoryLine, formatComment, formatDecision, formatDecisionLine, formatDocument, formatDocuments, formatGoal, formatGoalLine, formatInvocation, formatListingLine, formatMemberLine, formatMore, formatOptions, formatPersonLine, formatPersonWork, formatPlugin, formatPluginAsks, formatPluginGrantLine, formatPluginHelp, formatPluginRecord, formatPluginRecords, formatPluginRecordVersions, formatPluginSettingLine, formatPlugins, formatProject, formatProjectLine, formatRules, formatRun, formatRunLine, formatTask, formatTaskLine, formatTeamLine, formatVersion, formatVersionLine, formatWork, formatWorkspace, localized, moment, setMomentZone, } from "./format.js";
|
|
16
16
|
import { COMMAND_HELP, helpFor, ownSkillPath, usage } from "./help.js";
|
|
17
17
|
import { describeInstall, updateCommandFor } from "./install.js";
|
|
18
18
|
import { bodyOfFile, bodyOfFolder, bodyOfSkill, formatSkillBrowse, formatSkillFile, formatSkillFind, formatSkillLoad, formatSkills, joinFile, joinFolder, joinSkill, skillPathIn, summaryOf, } from "./pack.js";
|
|
@@ -373,10 +373,6 @@ function theTwo(positionals, ask) {
|
|
|
373
373
|
}
|
|
374
374
|
return [first, second];
|
|
375
375
|
}
|
|
376
|
-
/**
|
|
377
|
-
* The decision once answered, or as it stands when the wait is over. Each request asks the
|
|
378
|
-
* console to hold it for a while, so the answer arrives as soon as a person gives it.
|
|
379
|
-
*/
|
|
380
376
|
async function waitForAnswer(io, project, decision, seconds) {
|
|
381
377
|
const deadline = io.now() + seconds * 1000;
|
|
382
378
|
let current = decision;
|
|
@@ -1077,6 +1073,8 @@ const plugin = async (args, io) => {
|
|
|
1077
1073
|
answer(io, session, detail, () => `Disabled ${detail.key}: its commands are refused until it is enabled again; what the console kept of it stays.`);
|
|
1078
1074
|
return EXIT.ok;
|
|
1079
1075
|
}
|
|
1076
|
+
case "capabilities":
|
|
1077
|
+
return pluginCapabilities(io, session, client, rest);
|
|
1080
1078
|
case "settings":
|
|
1081
1079
|
return pluginSettings(io, session, client, rest);
|
|
1082
1080
|
case "secret":
|
|
@@ -1307,7 +1305,49 @@ function capabilityLines(capabilities) {
|
|
|
1307
1305
|
: ` ${capability}: opens ${words.opens}; a malicious plugin reaches ${words.reach}`;
|
|
1308
1306
|
});
|
|
1309
1307
|
}
|
|
1310
|
-
/**
|
|
1308
|
+
/** The capabilities `--consent` names, given again or with commas between. */
|
|
1309
|
+
function capabilitiesNamed(values) {
|
|
1310
|
+
return texts(values, "consent")
|
|
1311
|
+
.flatMap((value) => value.split(","))
|
|
1312
|
+
.map((capability) => capability.trim())
|
|
1313
|
+
.filter((capability) => capability.length > 0);
|
|
1314
|
+
}
|
|
1315
|
+
/** What a plugin requires, and which of what it can go without it holds, as `capabilities` prints it. */
|
|
1316
|
+
function capabilitiesHeld(detail) {
|
|
1317
|
+
const held = detail.consent ?? [];
|
|
1318
|
+
const optional = detail.optionalCapabilities ?? [];
|
|
1319
|
+
const named = (capabilities) => capabilities.length === 0 ? "none" : capabilities.join(", ");
|
|
1320
|
+
return [
|
|
1321
|
+
`Capabilities of ${detail.key}:`,
|
|
1322
|
+
` required: ${named(detail.capabilities)}`,
|
|
1323
|
+
` optional, on: ${named(optional.filter((capability) => held.includes(capability)))}`,
|
|
1324
|
+
` optional, off: ${named(optional.filter((capability) => !held.includes(capability)))}`,
|
|
1325
|
+
].join("\n");
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* What a plugin holds; with `--consent`, which of the optional capabilities it holds from now on,
|
|
1329
|
+
* `none` for none of them. An administrator's.
|
|
1330
|
+
*/
|
|
1331
|
+
async function pluginCapabilities(io, session, client, rest) {
|
|
1332
|
+
const { values, positionals } = parse(rest, { consent: { type: "string", multiple: true } });
|
|
1333
|
+
if (positionals.length > 0) {
|
|
1334
|
+
throw misuse(`Nothing follows capabilities but --consent: console plugin ${client.key} capabilities [--consent <capability>,...|none]`);
|
|
1335
|
+
}
|
|
1336
|
+
const detail = await client.get();
|
|
1337
|
+
const named = capabilitiesNamed(values);
|
|
1338
|
+
if (named.length === 0) {
|
|
1339
|
+
answer(io, session, detail, () => capabilitiesHeld(detail));
|
|
1340
|
+
return EXIT.ok;
|
|
1341
|
+
}
|
|
1342
|
+
const chosen = named.filter((capability) => capability !== "none");
|
|
1343
|
+
const changed = await client.updateConsent([
|
|
1344
|
+
...detail.capabilities,
|
|
1345
|
+
...chosen.filter((capability) => !detail.capabilities.includes(capability)),
|
|
1346
|
+
]);
|
|
1347
|
+
answer(io, session, changed, () => capabilitiesHeld(changed));
|
|
1348
|
+
return EXIT.ok;
|
|
1349
|
+
}
|
|
1350
|
+
/** Enables a plugin, with consent to every capability it requires, named again; an administrator's. */
|
|
1311
1351
|
async function pluginEnable(io, session, client, rest) {
|
|
1312
1352
|
const { values, positionals } = parse(rest, {
|
|
1313
1353
|
consent: { type: "string", multiple: true },
|
|
@@ -1320,16 +1360,17 @@ async function pluginEnable(io, session, client, rest) {
|
|
|
1320
1360
|
}
|
|
1321
1361
|
const [detail, me] = await Promise.all([client.get(), session.client.me()]);
|
|
1322
1362
|
const asked = detail.capabilities;
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
.map((capability) => capability.trim())
|
|
1326
|
-
.filter((capability) => capability.length > 0);
|
|
1363
|
+
const optional = detail.optionalCapabilities ?? [];
|
|
1364
|
+
const consent = capabilitiesNamed(values);
|
|
1327
1365
|
if (asked.length > 0 && !session.json) {
|
|
1328
|
-
io.stdout(`${detail.key}
|
|
1366
|
+
io.stdout(`${detail.key} requires ${asked.length === 1 ? "one capability" : `${asked.length} capabilities`}:\n${capabilityLines(asked).join("\n")}\n`);
|
|
1367
|
+
}
|
|
1368
|
+
if (optional.length > 0 && !session.json) {
|
|
1369
|
+
io.stdout(`It can go without these, which it holds if --consent names them too:\n${capabilityLines(optional).join("\n")}\n`);
|
|
1329
1370
|
}
|
|
1330
1371
|
const missing = asked.filter((capability) => !consent.includes(capability));
|
|
1331
1372
|
if (missing.length > 0) {
|
|
1332
|
-
throw misuse(`Enabling ${detail.key} needs consent to every capability it
|
|
1373
|
+
throw misuse(`Enabling ${detail.key} needs consent to every capability it requires, named again: --consent ${asked.join(",")}. Not named: ${missing.join(", ")}.`);
|
|
1333
1374
|
}
|
|
1334
1375
|
const boardFlag = text(values, "board");
|
|
1335
1376
|
let board = null;
|
|
@@ -1361,12 +1402,18 @@ async function pluginEnable(io, session, client, rest) {
|
|
|
1361
1402
|
});
|
|
1362
1403
|
answer(io, session, enabled, () => [
|
|
1363
1404
|
`Enabled ${enabled.key}${enabled.board === null ? "" : `, with the board project ${enabled.board.key}`}; on ${enabled.projects === "all" || enabled.projects === null ? "every project" : enabled.projects.join(", ") || "no project"}.`,
|
|
1405
|
+
...(enabled.needsTimeZone === true
|
|
1406
|
+
? [
|
|
1407
|
+
"The workspace has no time zone: the plugin's times of day run on UTC until an administrator sets one, with console workspace edit --time-zone <zone>.",
|
|
1408
|
+
]
|
|
1409
|
+
: []),
|
|
1364
1410
|
...(enabled.settings.length === 0
|
|
1365
1411
|
? []
|
|
1366
1412
|
: [
|
|
1367
1413
|
"Settings:",
|
|
1368
1414
|
...enabled.settings.map((setting) => formatPluginSettingLine(setting, me.language)),
|
|
1369
1415
|
]),
|
|
1416
|
+
...formatPluginAsks(enabled, me.language),
|
|
1370
1417
|
].join("\n"));
|
|
1371
1418
|
return EXIT.ok;
|
|
1372
1419
|
}
|
|
@@ -1411,18 +1458,31 @@ async function pluginSecret(io, session, client, rest) {
|
|
|
1411
1458
|
answer(io, session, { name, set: true }, () => `Set the secret ${name} of ${client.key}: sealed, and shown by nothing from now on.`);
|
|
1412
1459
|
return EXIT.ok;
|
|
1413
1460
|
}
|
|
1414
|
-
/**
|
|
1461
|
+
/**
|
|
1462
|
+
* Grants a role of the plugin to a person, to the members of a project, to another plugin, to the
|
|
1463
|
+
* members of a team, or to everyone; an administrator's.
|
|
1464
|
+
*/
|
|
1415
1465
|
async function pluginGrant(io, session, client, rest, projectKey) {
|
|
1416
1466
|
const { values, positionals } = parse(rest, {
|
|
1417
1467
|
person: { type: "string" },
|
|
1468
|
+
plugin: { type: "string" },
|
|
1469
|
+
team: { type: "string" },
|
|
1418
1470
|
everyone: { type: "boolean" },
|
|
1419
1471
|
});
|
|
1420
|
-
const usage = `console plugin ${client.key} grant <role> --person <login> | --project <key> | --everyone`;
|
|
1472
|
+
const usage = `console plugin ${client.key} grant <role> --person <login> | --project <key> | --plugin <key> | --team <key> | --everyone`;
|
|
1421
1473
|
const role = theOne(positionals, `Say which role: ${usage}`);
|
|
1422
1474
|
const login = text(values, "person");
|
|
1475
|
+
const other = text(values, "plugin");
|
|
1476
|
+
const teamKey = text(values, "team");
|
|
1423
1477
|
const everyone = on(values, "everyone");
|
|
1424
|
-
if ([
|
|
1425
|
-
|
|
1478
|
+
if ([
|
|
1479
|
+
login !== undefined,
|
|
1480
|
+
projectKey !== undefined,
|
|
1481
|
+
other !== undefined,
|
|
1482
|
+
teamKey !== undefined,
|
|
1483
|
+
everyone,
|
|
1484
|
+
].filter(Boolean).length !== 1) {
|
|
1485
|
+
throw misuse(`Say whom, one of the five: ${usage}`);
|
|
1426
1486
|
}
|
|
1427
1487
|
let input;
|
|
1428
1488
|
if (login !== undefined) {
|
|
@@ -1435,6 +1495,12 @@ async function pluginGrant(io, session, client, rest, projectKey) {
|
|
|
1435
1495
|
else if (projectKey !== undefined) {
|
|
1436
1496
|
input = { role, project: projectKey };
|
|
1437
1497
|
}
|
|
1498
|
+
else if (other !== undefined) {
|
|
1499
|
+
input = { role, plugin: other };
|
|
1500
|
+
}
|
|
1501
|
+
else if (teamKey !== undefined) {
|
|
1502
|
+
input = { role, team: teamKey };
|
|
1503
|
+
}
|
|
1438
1504
|
else {
|
|
1439
1505
|
input = { role, everyone: true };
|
|
1440
1506
|
}
|
|
@@ -1531,7 +1597,7 @@ const rules = async (args, io) => {
|
|
|
1531
1597
|
};
|
|
1532
1598
|
/**
|
|
1533
1599
|
* The rules as they read for the person whose token the command holds: `{language}` filled
|
|
1534
|
-
* with the language they
|
|
1600
|
+
* with the language they read in, or with the task's while they have none.
|
|
1535
1601
|
*/
|
|
1536
1602
|
function forPerson(half, language) {
|
|
1537
1603
|
return half.body === null ? half : { ...half, body: fillRules(half.body, language) };
|
|
@@ -1909,13 +1975,16 @@ const ask = async (args, io) => {
|
|
|
1909
1975
|
file: { type: "string" },
|
|
1910
1976
|
run: { type: "string" },
|
|
1911
1977
|
wait: { type: "string" },
|
|
1912
|
-
to: { type: "string" },
|
|
1978
|
+
to: { type: "string", multiple: true },
|
|
1913
1979
|
attach: { type: "string", multiple: true },
|
|
1914
1980
|
});
|
|
1915
1981
|
const question = theOne(positionals, 'Say the question: console ask "<question>" --option "<label>" --option "<label>"');
|
|
1916
|
-
const
|
|
1917
|
-
if (
|
|
1918
|
-
throw misuse("Say whom the decision is for: --to <login|me
|
|
1982
|
+
const tos = texts(values, "to");
|
|
1983
|
+
if (tos.includes("none")) {
|
|
1984
|
+
throw misuse("Say whom the decision is for: --to <login|me>, given again for several.");
|
|
1985
|
+
}
|
|
1986
|
+
if (tos.length > MAX_DECIDERS) {
|
|
1987
|
+
throw misuse(`Name up to ${MAX_DECIDERS} people with --to.`);
|
|
1919
1988
|
}
|
|
1920
1989
|
const options = texts(values, "option");
|
|
1921
1990
|
if (options.length < MIN_OPTIONS || options.length > MAX_OPTIONS) {
|
|
@@ -1928,20 +1997,31 @@ const ask = async (args, io) => {
|
|
|
1928
1997
|
const session = await open(io, values);
|
|
1929
1998
|
const project = await session.project();
|
|
1930
1999
|
const runId = await currentRun(project, values);
|
|
1931
|
-
// Whom the decision is for (ADR-0042): the person the agent works for, unless --to
|
|
1932
|
-
|
|
2000
|
+
// Whom the decision is for (ADR-0042, ADR-0057): the person the agent works for, unless --to
|
|
2001
|
+
// names others of the project, each once; several, and any of them may answer.
|
|
2002
|
+
const deciderIds = [];
|
|
2003
|
+
for (const to of tos) {
|
|
2004
|
+
const id = await personIdOf(session, to);
|
|
2005
|
+
if (typeof id === "string" && !deciderIds.includes(id)) {
|
|
2006
|
+
deciderIds.push(id);
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
1933
2009
|
const raised = await project.raiseDecision({
|
|
1934
2010
|
question,
|
|
1935
2011
|
options: options.map((label) => ({ label })),
|
|
1936
2012
|
runId,
|
|
1937
2013
|
...(body === undefined ? {} : { body }),
|
|
1938
|
-
...(
|
|
2014
|
+
...(deciderIds.length === 0
|
|
2015
|
+
? {}
|
|
2016
|
+
: deciderIds.length === 1
|
|
2017
|
+
? { deciderId: deciderIds[0] }
|
|
2018
|
+
: { deciderId: deciderIds[0], deciderIds }),
|
|
1939
2019
|
}, files);
|
|
1940
2020
|
const withFiles = files.length === 0 ? "" : `, with ${files.length} file${files.length === 1 ? "" : "s"}`;
|
|
1941
2021
|
// The id is printed before any waiting, so that a wait cut short loses nothing.
|
|
1942
2022
|
io.stdout(session.json
|
|
1943
2023
|
? `${JSON.stringify(raised, null, 2)}\n`
|
|
1944
|
-
: `Decision ${raised.id} raised on run ${runId}${withFiles}; the run waits for ${raised
|
|
2024
|
+
: `Decision ${raised.id} raised on run ${runId}${withFiles}; the run waits for ${deciderLogins(raised)}.\ndecision id: ${raised.id}\n${formatOptions(raised)}\n`);
|
|
1945
2025
|
return settle(io, session, project, raised, seconds, true);
|
|
1946
2026
|
};
|
|
1947
2027
|
/**
|
|
@@ -1964,14 +2044,74 @@ const answering = async (args, io) => {
|
|
|
1964
2044
|
}
|
|
1965
2045
|
const note = await bodyOf(io, values, text(values, "note"));
|
|
1966
2046
|
const session = await open(io, values);
|
|
1967
|
-
const
|
|
1968
|
-
const answered = await project.answerDecision(id, {
|
|
2047
|
+
const answered = await answerReached(session, id, {
|
|
1969
2048
|
...(own === undefined ? { option } : { own }),
|
|
1970
2049
|
...(note === undefined ? {} : { note }),
|
|
1971
2050
|
});
|
|
1972
2051
|
answer(io, session, answered, () => formatDecision(answered));
|
|
1973
2052
|
return EXIT.ok;
|
|
1974
2053
|
};
|
|
2054
|
+
/** Whether a refusal says the decision is not in the project the command works in, or the project is not the person's. */
|
|
2055
|
+
const notInProject = (error) => error instanceof ApiError &&
|
|
2056
|
+
(error.code === "decision.not_found" || error.code === "project.not_found");
|
|
2057
|
+
/**
|
|
2058
|
+
* One decision as the command reaches it (ADR-0068): in the project the command works in, as
|
|
2059
|
+
* before; else wherever it is, addressed to the person, raised by them, or a plugin's addressed
|
|
2060
|
+
* to someone they manage, through the console's own route for one decision. A plugin's decision
|
|
2061
|
+
* waits in the plugin's board project, which a checkout bound to another project does not name,
|
|
2062
|
+
* and which the person may not be in at all. A console from before that route answers as before.
|
|
2063
|
+
*/
|
|
2064
|
+
async function reachDecision(session, id) {
|
|
2065
|
+
if ((await session.projectKey()) !== undefined) {
|
|
2066
|
+
const project = await session.project();
|
|
2067
|
+
try {
|
|
2068
|
+
return { decision: await project.decision(id), waiter: project };
|
|
2069
|
+
}
|
|
2070
|
+
catch (error) {
|
|
2071
|
+
if (!notInProject(error)) {
|
|
2072
|
+
throw error;
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
const anywhere = session.client;
|
|
2077
|
+
try {
|
|
2078
|
+
const reached = await anywhere.decision(id);
|
|
2079
|
+
return {
|
|
2080
|
+
decision: reached.decision,
|
|
2081
|
+
waiter: {
|
|
2082
|
+
awaitDecision: async (decisionId, seconds) => (await anywhere.awaitDecision(decisionId, seconds)).decision,
|
|
2083
|
+
},
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
catch (error) {
|
|
2087
|
+
if (error instanceof ApiError && error.status === 404) {
|
|
2088
|
+
throw new ApiError(404, "decision.not_found", "The decision was not found.");
|
|
2089
|
+
}
|
|
2090
|
+
throw error;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
/** Writes an answer on a decision as the command reaches it (ADR-0068): in the project, else wherever it is. */
|
|
2094
|
+
async function answerReached(session, id, input) {
|
|
2095
|
+
if ((await session.projectKey()) !== undefined) {
|
|
2096
|
+
try {
|
|
2097
|
+
return await (await session.project()).answerDecision(id, input);
|
|
2098
|
+
}
|
|
2099
|
+
catch (error) {
|
|
2100
|
+
if (!notInProject(error)) {
|
|
2101
|
+
throw error;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
try {
|
|
2106
|
+
return await session.client.answerDecision(id, input);
|
|
2107
|
+
}
|
|
2108
|
+
catch (error) {
|
|
2109
|
+
if (error instanceof ApiError && error.status === 404) {
|
|
2110
|
+
throw new ApiError(404, "decision.not_found", "The decision was not found.");
|
|
2111
|
+
}
|
|
2112
|
+
throw error;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
1975
2115
|
const decision = async (args, io) => {
|
|
1976
2116
|
const { values, positionals } = parse(args, {
|
|
1977
2117
|
wait: { type: "string" },
|
|
@@ -1982,19 +2122,19 @@ const decision = async (args, io) => {
|
|
|
1982
2122
|
const seconds = secondsOf(text(values, "wait"), 0);
|
|
1983
2123
|
const outcome = await bodyOf(io, values, text(values, "outcome"));
|
|
1984
2124
|
const session = await open(io, values);
|
|
1985
|
-
const project = await session.project();
|
|
1986
2125
|
if (outcome !== undefined) {
|
|
1987
2126
|
// What followed the decision: the record grows, and is printed as it stands.
|
|
2127
|
+
const project = await session.project();
|
|
1988
2128
|
const grown = await project.updateDecision(id, { outcome });
|
|
1989
2129
|
answer(io, session, grown, () => formatDecision(grown));
|
|
1990
2130
|
return EXIT.ok;
|
|
1991
2131
|
}
|
|
1992
|
-
const found = await
|
|
2132
|
+
const { decision: found, waiter } = await reachDecision(session, id);
|
|
1993
2133
|
if (seconds === 0) {
|
|
1994
2134
|
answer(io, session, found, () => formatDecision(found));
|
|
1995
2135
|
return EXIT.ok;
|
|
1996
2136
|
}
|
|
1997
|
-
return settle(io, session,
|
|
2137
|
+
return settle(io, session, waiter, found, seconds);
|
|
1998
2138
|
};
|
|
1999
2139
|
const docs = async (args, io) => {
|
|
2000
2140
|
const { values, positionals } = parse(args, {
|
|
@@ -2396,9 +2536,68 @@ const people = async (args, io) => {
|
|
|
2396
2536
|
const { values } = parse(args, {}, false);
|
|
2397
2537
|
const session = await open(io, values);
|
|
2398
2538
|
const found = await session.client.people();
|
|
2399
|
-
|
|
2539
|
+
const loginOf = new Map(found.items.map((person) => [person.id, person.login]));
|
|
2540
|
+
answer(io, session, found, () => [
|
|
2541
|
+
...found.items.map((person) => formatPersonLine(person, person.managerId === null || person.managerId === undefined
|
|
2542
|
+
? undefined
|
|
2543
|
+
: (loginOf.get(person.managerId) ?? person.managerId))),
|
|
2544
|
+
...found.listed.map(formatListingLine),
|
|
2545
|
+
].join("\n"));
|
|
2400
2546
|
return EXIT.ok;
|
|
2401
2547
|
};
|
|
2548
|
+
/** The teams of the workspace (ADR-0073): each with who is in it, by login. */
|
|
2549
|
+
const teams = async (args, io) => {
|
|
2550
|
+
const { values } = parse(args, {}, false);
|
|
2551
|
+
const session = await open(io, values);
|
|
2552
|
+
const [found, everyone] = await Promise.all([session.client.teams(), session.client.people()]);
|
|
2553
|
+
const loginOf = new Map(everyone.items.map((person) => [person.id, person.login]));
|
|
2554
|
+
answer(io, session, found, () => found.items.length === 0
|
|
2555
|
+
? "No teams yet. console team new <key> <name> makes one."
|
|
2556
|
+
: found.items.map((team) => formatTeamLine(team, loginOf)).join("\n"));
|
|
2557
|
+
return EXIT.ok;
|
|
2558
|
+
};
|
|
2559
|
+
const TEAM_USAGE = 'Say what to do: console team new <key> "<name>" | team rename <key> "<new name>" | team remove <key> | team add <key> <login> | team drop <key> <login>';
|
|
2560
|
+
// A team is called by its key (ADR-0074), which never changes; its name is what people read.
|
|
2561
|
+
const team = async (args, io) => {
|
|
2562
|
+
const what = args[0];
|
|
2563
|
+
if (what === "new") {
|
|
2564
|
+
const { values, positionals } = parse(args.slice(1), {});
|
|
2565
|
+
const [key, name] = theTwo(positionals, 'Say the key and the name: console team new <key> "<name>"');
|
|
2566
|
+
const session = await open(io, values);
|
|
2567
|
+
const made = await session.client.createTeam({ key, name });
|
|
2568
|
+
answer(io, session, made, () => formatTeamLine(made, new Map()));
|
|
2569
|
+
return EXIT.ok;
|
|
2570
|
+
}
|
|
2571
|
+
if (what === "rename") {
|
|
2572
|
+
const { values, positionals } = parse(args.slice(1), {});
|
|
2573
|
+
const [key, next] = theTwo(positionals, 'Say which team and its new name: console team rename <key> "<new name>"');
|
|
2574
|
+
const session = await open(io, values);
|
|
2575
|
+
const changed = await session.client.renameTeam(key, { name: next });
|
|
2576
|
+
answer(io, session, changed, () => formatTeamLine(changed, new Map()));
|
|
2577
|
+
return EXIT.ok;
|
|
2578
|
+
}
|
|
2579
|
+
if (what === "remove") {
|
|
2580
|
+
const { values, positionals } = parse(args.slice(1), {});
|
|
2581
|
+
const key = theOne(positionals, "Say which team: console team remove <key>");
|
|
2582
|
+
const session = await open(io, values);
|
|
2583
|
+
await session.client.removeTeam(key);
|
|
2584
|
+
answer(io, session, { removed: key }, () => `The team ${key} is no more.`);
|
|
2585
|
+
return EXIT.ok;
|
|
2586
|
+
}
|
|
2587
|
+
if (what === "add" || what === "drop") {
|
|
2588
|
+
const { values, positionals } = parse(args.slice(1), {});
|
|
2589
|
+
const [key, login] = theTwo(positionals, `Say which team and whom: console team ${what} <key> <login>`);
|
|
2590
|
+
const session = await open(io, values);
|
|
2591
|
+
const person = await personOf(session, login);
|
|
2592
|
+
const changed = what === "add"
|
|
2593
|
+
? await session.client.addTeamMember(key, person.id)
|
|
2594
|
+
: await session.client.removeTeamMember(key, person.id);
|
|
2595
|
+
const everyone = await session.client.people();
|
|
2596
|
+
answer(io, session, changed, () => formatTeamLine(changed, new Map(everyone.items.map((one) => [one.id, one.login]))));
|
|
2597
|
+
return EXIT.ok;
|
|
2598
|
+
}
|
|
2599
|
+
throw misuse(TEAM_USAGE);
|
|
2600
|
+
};
|
|
2402
2601
|
// The workspace's settings (ADR-0035): read by everyone, changed by an administrator, themselves
|
|
2403
2602
|
// on the Settings page or through their agent here (ADR-0021).
|
|
2404
2603
|
const WORKSPACE_FLAGS = "--people-visibility <everyone|shared-projects>, --week-start <auto|monday|sunday>, --time-zone <zone|none>";
|
|
@@ -2459,7 +2658,7 @@ const workspace = async (args, io) => {
|
|
|
2459
2658
|
}
|
|
2460
2659
|
throw misuse(WORKSPACE_USAGE);
|
|
2461
2660
|
};
|
|
2462
|
-
const PERSON_USAGE = "Say what to do: console person role <login> <admin|member> | person add <login> [--host gh|google] | person remove <login> | person deactivate <login> | person reactivate <login>";
|
|
2661
|
+
const PERSON_USAGE = "Say what to do: console person role <login> <admin|member> | person manager <login> <manager login|none> | person email <login> <address|none> | person add <login> [--host gh|google] | person remove <login> | person deactivate <login> | person reactivate <login>";
|
|
2463
2662
|
const person = async (args, io) => {
|
|
2464
2663
|
const what = args[0];
|
|
2465
2664
|
if (what === "role") {
|
|
@@ -2472,6 +2671,27 @@ const person = async (args, io) => {
|
|
|
2472
2671
|
answer(io, session, changed, () => formatPersonLine(changed));
|
|
2473
2672
|
return EXIT.ok;
|
|
2474
2673
|
}
|
|
2674
|
+
if (what === "email") {
|
|
2675
|
+
const { values, positionals } = parse(args.slice(1), {});
|
|
2676
|
+
const [login, address] = theTwo(positionals, "Say whom and their address: console person email <login> <address|none>");
|
|
2677
|
+
const session = await open(io, values);
|
|
2678
|
+
const found = await personOf(session, login);
|
|
2679
|
+
const changed = await session.client.updatePerson(found.id, {
|
|
2680
|
+
email: address === "none" ? null : address,
|
|
2681
|
+
});
|
|
2682
|
+
answer(io, session, changed, () => formatPersonLine(changed));
|
|
2683
|
+
return EXIT.ok;
|
|
2684
|
+
}
|
|
2685
|
+
if (what === "manager") {
|
|
2686
|
+
const { values, positionals } = parse(args.slice(1), {});
|
|
2687
|
+
const [login, manager] = theTwo(positionals, "Say whom and whom they report to: console person manager <login> <manager login|none>");
|
|
2688
|
+
const session = await open(io, values);
|
|
2689
|
+
const found = await personOf(session, login);
|
|
2690
|
+
const managerId = manager === "none" ? null : (await personOf(session, manager)).id;
|
|
2691
|
+
const changed = await session.client.updatePerson(found.id, { managerId });
|
|
2692
|
+
answer(io, session, changed, () => formatPersonLine(changed, managerId === null ? undefined : manager));
|
|
2693
|
+
return EXIT.ok;
|
|
2694
|
+
}
|
|
2475
2695
|
if (what === "add") {
|
|
2476
2696
|
const { values, positionals } = parse(args.slice(1), { host: { type: "string" } });
|
|
2477
2697
|
const login = theOne(positionals, "Say whom: console person add <login> [--host gh|google]");
|
|
@@ -2817,6 +3037,8 @@ const COMMANDS = {
|
|
|
2817
3037
|
plugin,
|
|
2818
3038
|
people,
|
|
2819
3039
|
person,
|
|
3040
|
+
teams,
|
|
3041
|
+
team,
|
|
2820
3042
|
workspace,
|
|
2821
3043
|
project,
|
|
2822
3044
|
members,
|