@skillcdn/console 0.1.12 → 0.1.13
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 +5 -3
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +260 -6
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts +7 -1
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +22 -0
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +69 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/console.d.ts +3 -2
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +12 -46
- package/dist/console.js.map +1 -1
- package/dist/i18n/en.js +2 -2
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/ko.js +2 -2
- package/dist/i18n/ko.js.map +1 -1
- package/docs/specs/cli.md +15 -2
- package/docs/specs/rest.md +13 -13
- package/package.json +1 -1
- package/skills/building-a-console/SKILL.md +2 -2
- package/skills/working-the-board/SKILL.md +5 -1
- package/src/cli/cli.ts +333 -5
- package/src/cli/format.ts +27 -0
- package/src/cli/help.ts +86 -1
- package/src/console.tsx +11 -49
- package/src/i18n/en.ts +2 -2
- package/src/i18n/ko.ts +2 -2
package/src/cli/help.ts
CHANGED
|
@@ -3,6 +3,9 @@ import {
|
|
|
3
3
|
MAX_AGENT_LENGTH,
|
|
4
4
|
MAX_ARTIFACTS_PER_RUN,
|
|
5
5
|
MAX_BODY_LENGTH,
|
|
6
|
+
MAX_CATEGORIES,
|
|
7
|
+
MAX_CATEGORY_KEY_LENGTH,
|
|
8
|
+
MAX_CATEGORY_NAME_LENGTH,
|
|
6
9
|
MAX_DOCUMENT_LENGTH,
|
|
7
10
|
MAX_DOCUMENT_PATH_LENGTH,
|
|
8
11
|
MAX_FILE_BYTES,
|
|
@@ -10,6 +13,11 @@ import {
|
|
|
10
13
|
MAX_LINK_LABEL_LENGTH,
|
|
11
14
|
MAX_OPTION_LABEL_LENGTH,
|
|
12
15
|
MAX_OPTIONS,
|
|
16
|
+
MAX_PROJECT_DESCRIPTION_LENGTH,
|
|
17
|
+
MAX_PROJECT_ICON_BYTES,
|
|
18
|
+
MAX_PROJECT_KEY_LENGTH,
|
|
19
|
+
MAX_PROJECT_NAME_LENGTH,
|
|
20
|
+
MAX_PROJECT_SYMBOL_LENGTH,
|
|
13
21
|
MAX_QUESTION_LENGTH,
|
|
14
22
|
MAX_REPORTS_PER_RUN,
|
|
15
23
|
MAX_SUMMARY_LENGTH,
|
|
@@ -17,6 +25,7 @@ import {
|
|
|
17
25
|
MAX_VERSIONS_PER_DOCUMENT,
|
|
18
26
|
MIN_OPTIONS,
|
|
19
27
|
} from "../limits.js";
|
|
28
|
+
import { COLOR_NAMES } from "../vocabulary.js";
|
|
20
29
|
|
|
21
30
|
// What the command says about itself: for an agent that meets it for the first time, and for a
|
|
22
31
|
// person. One table for every command, from which the usage and each command's help are made:
|
|
@@ -53,6 +62,8 @@ const DOCUMENT_PATH = `a path of lowercase letters, digits and hyphens, with sla
|
|
|
53
62
|
const DOCUMENT_REFUSALS = ["document.not_found: no page has that path in this project"];
|
|
54
63
|
|
|
55
64
|
const DAY = "a day as 2026-10-24, today, tomorrow, or +7 for seven days from today";
|
|
65
|
+
const COLORS = `one of ${COLOR_NAMES.join(", ")}`;
|
|
66
|
+
const KEY = "lowercase letters, digits and hyphens";
|
|
56
67
|
const TASK_FIELD_FLAGS =
|
|
57
68
|
"[--state <state>] [--priority <priority>] [--category <key>] [--goal <id>] [--starts <day>] [--due <day>] [--assignee <login|me>] [--parent <number|id>]";
|
|
58
69
|
const TASK_FIELD_LIMITS = [
|
|
@@ -143,7 +154,76 @@ export const COMMAND_HELP: Readonly<Record<string, CommandHelp>> = {
|
|
|
143
154
|
categories: {
|
|
144
155
|
usage: "categories",
|
|
145
156
|
about:
|
|
146
|
-
"The categories tasks are sorted into, the same in every project: each with its key, which --category takes, its name and its colour. An administrator keeps them
|
|
157
|
+
"The categories tasks are sorted into, the same in every project: each with its key, which --category takes, its name and its colour. An administrator keeps them: console category, or the Categories page.",
|
|
158
|
+
},
|
|
159
|
+
people: {
|
|
160
|
+
usage: "people",
|
|
161
|
+
about:
|
|
162
|
+
"Everyone of the workspace, by login, with their name where the host gives one and what they are in it: admin, who configures the workspace, or member, who works in the projects they are in. The logins are what --assignee, member and person role take.",
|
|
163
|
+
},
|
|
164
|
+
person: {
|
|
165
|
+
usage: "person role <login> <admin|member>",
|
|
166
|
+
about:
|
|
167
|
+
"Says what a person is in the workspace, for an administrator: the person themselves on the People page, or their agent here, named on it. The board keeps at least one administrator.",
|
|
168
|
+
refusals: [
|
|
169
|
+
"auth.forbidden: only an administrator says what a person is",
|
|
170
|
+
"person.last_admin: the board keeps at least one administrator; make another one first",
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
project: {
|
|
174
|
+
usage:
|
|
175
|
+
'project <key> | project new <key> "<name>" [--description <markdown>|--file <path>] [--visibility workspace|private] [--skills <address>|none] [--color <name>] [--symbol <text>] | project edit <key> [--name "<name>"] [the same flags; none clears] | project icon <key> <file> | project icon <key> --remove',
|
|
176
|
+
about:
|
|
177
|
+
"One project in full; or a project made, with you as its owner, private unless --visibility workspace says everyone of the workspace works in it; or one changed, only in what the flags say, for an owner; or its image for its mark, set from a file of this machine or taken away. The organization's skills are the project's unless --skills names an address of its own.",
|
|
178
|
+
limits: [
|
|
179
|
+
`key: ${KEY}, up to ${MAX_PROJECT_KEY_LENGTH} characters; it does not change once made`,
|
|
180
|
+
`name: one line, up to ${MAX_PROJECT_NAME_LENGTH} characters; description: up to ${MAX_PROJECT_DESCRIPTION_LENGTH}`,
|
|
181
|
+
`color: ${COLORS}; symbol: up to ${MAX_PROJECT_SYMBOL_LENGTH} characters without spaces, shown on the colour; left out, the pages choose both from the key and the name`,
|
|
182
|
+
`image: a PNG, a JPEG, a GIF or a WebP of at most ${MAX_PROJECT_ICON_BYTES} bytes`,
|
|
183
|
+
],
|
|
184
|
+
refusals: [
|
|
185
|
+
"project.not_found: no project has that key, or it is not yours to see",
|
|
186
|
+
"auth.forbidden: only an owner of the project changes it",
|
|
187
|
+
"project.key_taken: a project has that key already",
|
|
188
|
+
"project.invalid_address: the skills address is not one as SkillCDN spells it",
|
|
189
|
+
"project.invalid_icon: the file is not an image of those kinds, by its bytes",
|
|
190
|
+
"request.too_large: the image is over the limit",
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
members: {
|
|
194
|
+
usage: "members",
|
|
195
|
+
about:
|
|
196
|
+
"Those listed in the project, by login, with what each is in it (owner, who configures it; member, who works on it) and since when. A project open to the workspace has everyone of it as a member besides, and an administrator owns every project.",
|
|
197
|
+
},
|
|
198
|
+
member: {
|
|
199
|
+
usage:
|
|
200
|
+
"member add <login> [--role owner|member] | member role <login> <owner|member> | member remove <login>",
|
|
201
|
+
about:
|
|
202
|
+
"Lists a person of the workspace in the project, as a member unless --role says owner; changes what they are in it; or takes them off the list. For an owner of the project.",
|
|
203
|
+
refusals: [
|
|
204
|
+
"auth.forbidden: only an owner of the project lists its members",
|
|
205
|
+
"member.invalid_person: the person is not of the workspace",
|
|
206
|
+
"member.exists: the person is listed already; member role changes what they are",
|
|
207
|
+
"member.not_found: the person is not listed in the project",
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
category: {
|
|
211
|
+
usage:
|
|
212
|
+
'category new <key> "<name>" [--color <name>] | category edit <key> [--name "<name>"] [--color <name>] | category remove <key>',
|
|
213
|
+
about:
|
|
214
|
+
"Makes a category tasks are sorted into, the same in every project; changes its name or its colour; or removes one no task is sorted into. For an administrator.",
|
|
215
|
+
limits: [
|
|
216
|
+
`key: ${KEY}, up to ${MAX_CATEGORY_KEY_LENGTH} characters; it does not change once made`,
|
|
217
|
+
`name: one line, up to ${MAX_CATEGORY_NAME_LENGTH} characters; color: ${COLORS}, gray when left out`,
|
|
218
|
+
`at most ${MAX_CATEGORIES} categories in the workspace`,
|
|
219
|
+
],
|
|
220
|
+
refusals: [
|
|
221
|
+
"auth.forbidden: only an administrator keeps the categories",
|
|
222
|
+
"category.key_taken: a category has that key already",
|
|
223
|
+
"category.not_found: no category has that key",
|
|
224
|
+
"category.in_use: tasks are sorted into it; move them out first (console task edit <number> --category <other|none>)",
|
|
225
|
+
"category.too_many: the workspace holds as many categories as one may",
|
|
226
|
+
],
|
|
147
227
|
},
|
|
148
228
|
skills: {
|
|
149
229
|
usage: "skills",
|
|
@@ -352,6 +432,11 @@ const SECTIONS: readonly { readonly title: string; readonly commands: readonly s
|
|
|
352
432
|
title: "The documents: the project's pages of Markdown, in folders, by path",
|
|
353
433
|
commands: ["docs", "doc", "write", "attach", "archive", "restore"],
|
|
354
434
|
},
|
|
435
|
+
{
|
|
436
|
+
title:
|
|
437
|
+
"Configuring, as the person: what an administrator or an owner does on the pages, their agent does here, named on it",
|
|
438
|
+
commands: ["people", "person", "project", "members", "member", "category"],
|
|
439
|
+
},
|
|
355
440
|
];
|
|
356
441
|
|
|
357
442
|
/** The whole usage, as `console help` prints it, naming the version when it is known. */
|
package/src/console.tsx
CHANGED
|
@@ -332,13 +332,12 @@ function holdsToken(data: ConsoleData): boolean {
|
|
|
332
332
|
}
|
|
333
333
|
|
|
334
334
|
/**
|
|
335
|
-
* The pages a console that holds a token does not offer: the tokens themselves and
|
|
336
|
-
*
|
|
335
|
+
* The pages a console that holds a token does not offer: the tokens themselves, and the
|
|
336
|
+
* approval of an agent's connection, which makes one; a person's own doing on the console's
|
|
337
|
+
* own pages, signed in. Everything else a person may do, their agent may (ADR-0021).
|
|
337
338
|
*/
|
|
338
339
|
export function onlyOnOwnPages(route: Route): boolean {
|
|
339
340
|
return (
|
|
340
|
-
route.name === "new-project" ||
|
|
341
|
-
route.name === "settings" ||
|
|
342
341
|
route.name === "agents" ||
|
|
343
342
|
route.name === "new-token" ||
|
|
344
343
|
route.name === "person-agents" ||
|
|
@@ -351,28 +350,6 @@ function NotWithToken() {
|
|
|
351
350
|
return <EmptyState title={words.title} body={words.body} />;
|
|
352
351
|
}
|
|
353
352
|
|
|
354
|
-
/**
|
|
355
|
-
* Where a console that holds a token keeps the person's choice of language: the browser's own
|
|
356
|
-
* storage, which may be absent or refuse, and then the browser's languages decide.
|
|
357
|
-
*/
|
|
358
|
-
const LANGUAGE_KEY = "sc-language";
|
|
359
|
-
|
|
360
|
-
function storedLanguage(): string | undefined {
|
|
361
|
-
try {
|
|
362
|
-
return globalThis.localStorage?.getItem(LANGUAGE_KEY) ?? undefined;
|
|
363
|
-
} catch {
|
|
364
|
-
return undefined;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
function storeLanguage(tag: string): void {
|
|
369
|
-
try {
|
|
370
|
-
globalThis.localStorage?.setItem(LANGUAGE_KEY, tag);
|
|
371
|
-
} catch {
|
|
372
|
-
// A browser that keeps nothing: the choice holds for this page.
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
353
|
/** A link that reads as a button: where a form is, for instance. */
|
|
377
354
|
function LinkButton(props: {
|
|
378
355
|
readonly href: string;
|
|
@@ -397,12 +374,11 @@ function ProjectsPage(props: {
|
|
|
397
374
|
const { data, components, navigation, making } = props;
|
|
398
375
|
const { busy, error, act } = useAction();
|
|
399
376
|
const href = (project: RestProject) => projectHref(project.key);
|
|
400
|
-
const configures = !holdsToken(data);
|
|
401
377
|
return (
|
|
402
378
|
<>
|
|
403
379
|
<div className="sc-page-head">
|
|
404
380
|
<h1 className="sc-page-title">{words.title}</h1>
|
|
405
|
-
{!making &&
|
|
381
|
+
{!making && (
|
|
406
382
|
<LinkButton href={PATHS.newProject} variant="primary">
|
|
407
383
|
{words.newProject}
|
|
408
384
|
</LinkButton>
|
|
@@ -435,11 +411,7 @@ function ProjectsPage(props: {
|
|
|
435
411
|
<EmptyState
|
|
436
412
|
title={words.empty.title}
|
|
437
413
|
body={words.empty.body}
|
|
438
|
-
action={
|
|
439
|
-
configures ? (
|
|
440
|
-
<LinkButton href={PATHS.newProject}>{words.newProject}</LinkButton>
|
|
441
|
-
) : undefined
|
|
442
|
-
}
|
|
414
|
+
action={<LinkButton href={PATHS.newProject}>{words.newProject}</LinkButton>}
|
|
443
415
|
/>
|
|
444
416
|
}
|
|
445
417
|
/>
|
|
@@ -966,7 +938,7 @@ function ProjectPage(props: {
|
|
|
966
938
|
}
|
|
967
939
|
if (route.name === "members") {
|
|
968
940
|
const me = data.me?.person ?? undefined;
|
|
969
|
-
const configures = owner
|
|
941
|
+
const configures = owner;
|
|
970
942
|
const members = words.projects.members;
|
|
971
943
|
return (
|
|
972
944
|
<>
|
|
@@ -1200,7 +1172,7 @@ function WorkspacePage(props: {
|
|
|
1200
1172
|
}
|
|
1201
1173
|
if (route.name === "categories") {
|
|
1202
1174
|
const me = data.me?.person ?? undefined;
|
|
1203
|
-
const administrator = me?.role === "admin"
|
|
1175
|
+
const administrator = me?.role === "admin";
|
|
1204
1176
|
const categories = words.categories;
|
|
1205
1177
|
return (
|
|
1206
1178
|
<>
|
|
@@ -1239,7 +1211,7 @@ function WorkspacePage(props: {
|
|
|
1239
1211
|
|
|
1240
1212
|
if (route.name === "people") {
|
|
1241
1213
|
const me = data.me?.person ?? undefined;
|
|
1242
|
-
const administrator = me?.role === "admin"
|
|
1214
|
+
const administrator = me?.role === "admin";
|
|
1243
1215
|
return (
|
|
1244
1216
|
<>
|
|
1245
1217
|
<div className="sc-page-head">
|
|
@@ -1334,10 +1306,7 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
1334
1306
|
const title = data.me?.workspace.name ?? fallbackTitle;
|
|
1335
1307
|
const viaToken = holdsToken(data);
|
|
1336
1308
|
// The language: the person's choice, else the browser's, else the first the console speaks.
|
|
1337
|
-
|
|
1338
|
-
// browser instead.
|
|
1339
|
-
const [ownChoice, setOwnChoice] = useState<string | undefined>(storedLanguage);
|
|
1340
|
-
const chosen = viaToken ? (ownChoice ?? data.me?.language) : data.me?.language;
|
|
1309
|
+
const chosen = data.me?.language;
|
|
1341
1310
|
const pack = useMemo(
|
|
1342
1311
|
() =>
|
|
1343
1312
|
chooseLanguage(
|
|
@@ -1485,7 +1454,7 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
1485
1454
|
label: words.nav.members,
|
|
1486
1455
|
current: route.name === "members",
|
|
1487
1456
|
},
|
|
1488
|
-
...(project?.role === "owner"
|
|
1457
|
+
...(project?.role === "owner"
|
|
1489
1458
|
? [
|
|
1490
1459
|
{
|
|
1491
1460
|
href: projectHref(projectKey, "settings"),
|
|
@@ -1591,14 +1560,7 @@ export function createConsole(config: ConsoleConfig = {}): ConsoleApp {
|
|
|
1591
1560
|
label: candidate.label,
|
|
1592
1561
|
current: candidate.tag === pack.tag,
|
|
1593
1562
|
}))}
|
|
1594
|
-
onLanguage={(tag) => {
|
|
1595
|
-
if (viaToken) {
|
|
1596
|
-
storeLanguage(tag);
|
|
1597
|
-
setOwnChoice(tag);
|
|
1598
|
-
} else {
|
|
1599
|
-
void data.actions.updateMe({ language: tag }).catch(() => undefined);
|
|
1600
|
-
}
|
|
1601
|
-
}}
|
|
1563
|
+
onLanguage={(tag) => void data.actions.updateMe({ language: tag }).catch(() => undefined)}
|
|
1602
1564
|
person={data.me.person}
|
|
1603
1565
|
live={projectKey === undefined ? undefined : data.live}
|
|
1604
1566
|
wide={
|
package/src/i18n/en.ts
CHANGED
|
@@ -89,7 +89,7 @@ export const en = {
|
|
|
89
89
|
},
|
|
90
90
|
notWithToken: {
|
|
91
91
|
title: "Done on the console's own pages",
|
|
92
|
-
body: "This console holds a token, which acts as you on the board
|
|
92
|
+
body: "This console holds a token, which acts as you on the board, configuring included. Agents are connected, and tokens made and removed, on the organization's console, signed in.",
|
|
93
93
|
},
|
|
94
94
|
you: "(you)",
|
|
95
95
|
nobody: "Nobody",
|
|
@@ -684,7 +684,7 @@ export const en = {
|
|
|
684
684
|
lookingUp: "Looking the code up",
|
|
685
685
|
notACode: "That is not a code: eight letters and digits, in two groups.",
|
|
686
686
|
noSuchCode:
|
|
687
|
-
"No agent asks to connect with this code. A code is good for
|
|
687
|
+
"No agent asks to connect with this code. A code is good for thirty minutes and for one approval; ask your agent for a new one.",
|
|
688
688
|
couldNotAsk: "The console could not be asked.",
|
|
689
689
|
asks: {
|
|
690
690
|
title: "An agent asks to connect as you",
|
package/src/i18n/ko.ts
CHANGED
|
@@ -62,7 +62,7 @@ export const ko: Messages = {
|
|
|
62
62
|
},
|
|
63
63
|
notWithToken: {
|
|
64
64
|
title: "콘솔 자체 페이지에서 하는 일입니다",
|
|
65
|
-
body: "이 콘솔은 토큰으로 당신을 대신해 보드를
|
|
65
|
+
body: "이 콘솔은 토큰으로 당신을 대신해 보드를 쓰며, 설정도 그렇게 합니다. 에이전트 연결과 토큰 관리는 조직 콘솔에 로그인해서 합니다.",
|
|
66
66
|
},
|
|
67
67
|
you: "(나)",
|
|
68
68
|
nobody: "없음",
|
|
@@ -652,7 +652,7 @@ export const ko: Messages = {
|
|
|
652
652
|
lookingUp: "코드를 찾는 중",
|
|
653
653
|
notACode: "코드가 아닙니다. 영문자와 숫자 여덟 자, 두 묶음입니다.",
|
|
654
654
|
noSuchCode:
|
|
655
|
-
"이 코드로 연결을 요청한 에이전트가 없습니다. 코드는
|
|
655
|
+
"이 코드로 연결을 요청한 에이전트가 없습니다. 코드는 30분 동안, 한 번의 승인에만 유효합니다. 에이전트에게 새 코드를 요청하세요.",
|
|
656
656
|
couldNotAsk: "콘솔에 물어볼 수 없습니다.",
|
|
657
657
|
asks: {
|
|
658
658
|
title: "에이전트가 나로서 연결하기를 요청합니다",
|