@skillcdn/console 0.4.0 → 0.4.2

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.
Files changed (308) hide show
  1. package/README.md +20 -2
  2. package/SKILLCDN.md +7 -5
  3. package/dist/api.d.ts +5 -4
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/api.js +5 -4
  6. package/dist/api.js.map +1 -1
  7. package/dist/cli/cli.d.ts.map +1 -1
  8. package/dist/cli/cli.js +579 -45
  9. package/dist/cli/cli.js.map +1 -1
  10. package/dist/cli/format.d.ts +34 -3
  11. package/dist/cli/format.d.ts.map +1 -1
  12. package/dist/cli/format.js +277 -6
  13. package/dist/cli/format.js.map +1 -1
  14. package/dist/cli/help.d.ts.map +1 -1
  15. package/dist/cli/help.js +60 -7
  16. package/dist/cli/help.js.map +1 -1
  17. package/dist/cli/plugin-check.d.ts +21 -0
  18. package/dist/cli/plugin-check.d.ts.map +1 -0
  19. package/dist/cli/plugin-check.js +374 -0
  20. package/dist/cli/plugin-check.js.map +1 -0
  21. package/dist/cli/plugin-cli.d.ts +20 -0
  22. package/dist/cli/plugin-cli.d.ts.map +1 -0
  23. package/dist/cli/plugin-cli.js +123 -0
  24. package/dist/cli/plugin-cli.js.map +1 -0
  25. package/dist/cli/plugin-init.d.ts +20 -0
  26. package/dist/cli/plugin-init.d.ts.map +1 -0
  27. package/dist/cli/plugin-init.js +74 -0
  28. package/dist/cli/plugin-init.js.map +1 -0
  29. package/dist/cli/plugin-main.d.ts +3 -0
  30. package/dist/cli/plugin-main.d.ts.map +1 -0
  31. package/dist/cli/plugin-main.js +28 -0
  32. package/dist/cli/plugin-main.js.map +1 -0
  33. package/dist/client.d.ts +109 -2
  34. package/dist/client.d.ts.map +1 -1
  35. package/dist/client.js +67 -2
  36. package/dist/client.js.map +1 -1
  37. package/dist/compat.d.ts +1 -1
  38. package/dist/compat.js +2 -2
  39. package/dist/components/board.d.ts +8 -0
  40. package/dist/components/board.d.ts.map +1 -1
  41. package/dist/components/board.js +59 -15
  42. package/dist/components/board.js.map +1 -1
  43. package/dist/components/event-feed.d.ts +1 -1
  44. package/dist/components/event-feed.d.ts.map +1 -1
  45. package/dist/components/event-feed.js +33 -1
  46. package/dist/components/event-feed.js.map +1 -1
  47. package/dist/components/markdown.d.ts.map +1 -1
  48. package/dist/components/markdown.js +6 -3
  49. package/dist/components/markdown.js.map +1 -1
  50. package/dist/components/pack-connect.d.ts +3 -1
  51. package/dist/components/pack-connect.d.ts.map +1 -1
  52. package/dist/components/pack-connect.js +4 -4
  53. package/dist/components/pack-connect.js.map +1 -1
  54. package/dist/components/pack-token.d.ts +3 -1
  55. package/dist/components/pack-token.d.ts.map +1 -1
  56. package/dist/components/pack-token.js +1 -1
  57. package/dist/components/pack-token.js.map +1 -1
  58. package/dist/components/people.d.ts.map +1 -1
  59. package/dist/components/people.js +14 -3
  60. package/dist/components/people.js.map +1 -1
  61. package/dist/components/plugin-frame.d.ts +52 -0
  62. package/dist/components/plugin-frame.d.ts.map +1 -0
  63. package/dist/components/plugin-frame.js +314 -0
  64. package/dist/components/plugin-frame.js.map +1 -0
  65. package/dist/components/plugin-settings.d.ts +38 -0
  66. package/dist/components/plugin-settings.d.ts.map +1 -0
  67. package/dist/components/plugin-settings.js +348 -0
  68. package/dist/components/plugin-settings.js.map +1 -0
  69. package/dist/components/remote-pictures.d.ts +6 -0
  70. package/dist/components/remote-pictures.d.ts.map +1 -0
  71. package/dist/components/remote-pictures.js +8 -0
  72. package/dist/components/remote-pictures.js.map +1 -0
  73. package/dist/components/shell.d.ts +2 -0
  74. package/dist/components/shell.d.ts.map +1 -1
  75. package/dist/components/shell.js +26 -1
  76. package/dist/components/shell.js.map +1 -1
  77. package/dist/components/sign-in.d.ts.map +1 -1
  78. package/dist/components/sign-in.js +49 -2
  79. package/dist/components/sign-in.js.map +1 -1
  80. package/dist/components/ui.d.ts +3 -2
  81. package/dist/components/ui.d.ts.map +1 -1
  82. package/dist/components/ui.js +6 -3
  83. package/dist/components/ui.js.map +1 -1
  84. package/dist/components/workspace.d.ts +7 -1
  85. package/dist/components/workspace.d.ts.map +1 -1
  86. package/dist/components/workspace.js +1 -1
  87. package/dist/components/workspace.js.map +1 -1
  88. package/dist/console.css +384 -5
  89. package/dist/console.d.ts.map +1 -1
  90. package/dist/console.js +146 -29
  91. package/dist/console.js.map +1 -1
  92. package/dist/data.d.ts +11 -2
  93. package/dist/data.d.ts.map +1 -1
  94. package/dist/data.js +35 -21
  95. package/dist/data.js.map +1 -1
  96. package/dist/i18n/en.d.ts +163 -1
  97. package/dist/i18n/en.d.ts.map +1 -1
  98. package/dist/i18n/en.js +174 -1
  99. package/dist/i18n/en.js.map +1 -1
  100. package/dist/i18n/ko.d.ts.map +1 -1
  101. package/dist/i18n/ko.js +154 -1
  102. package/dist/i18n/ko.js.map +1 -1
  103. package/dist/index.d.ts +3 -1
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +3 -1
  106. package/dist/index.js.map +1 -1
  107. package/dist/limits.d.ts +14 -0
  108. package/dist/limits.d.ts.map +1 -1
  109. package/dist/limits.js +14 -0
  110. package/dist/limits.js.map +1 -1
  111. package/dist/plugin/args.d.ts +148 -0
  112. package/dist/plugin/args.d.ts.map +1 -0
  113. package/dist/plugin/args.js +369 -0
  114. package/dist/plugin/args.js.map +1 -0
  115. package/dist/plugin/bridge.d.ts +146 -0
  116. package/dist/plugin/bridge.d.ts.map +1 -0
  117. package/dist/plugin/bridge.js +125 -0
  118. package/dist/plugin/bridge.js.map +1 -0
  119. package/dist/plugin/capabilities.d.ts +20 -0
  120. package/dist/plugin/capabilities.d.ts.map +1 -0
  121. package/dist/plugin/capabilities.js +133 -0
  122. package/dist/plugin/capabilities.js.map +1 -0
  123. package/dist/plugin/context.d.ts +374 -0
  124. package/dist/plugin/context.d.ts.map +1 -0
  125. package/dist/plugin/context.js +2 -0
  126. package/dist/plugin/context.js.map +1 -0
  127. package/dist/plugin/define.d.ts +208 -0
  128. package/dist/plugin/define.d.ts.map +1 -0
  129. package/dist/plugin/define.js +482 -0
  130. package/dist/plugin/define.js.map +1 -0
  131. package/dist/plugin/definition-error.d.ts +11 -0
  132. package/dist/plugin/definition-error.d.ts.map +1 -0
  133. package/dist/plugin/definition-error.js +17 -0
  134. package/dist/plugin/definition-error.js.map +1 -0
  135. package/dist/plugin/limits.d.ts +67 -0
  136. package/dist/plugin/limits.d.ts.map +1 -0
  137. package/dist/plugin/limits.js +70 -0
  138. package/dist/plugin/limits.js.map +1 -0
  139. package/dist/plugin/load-worker.d.ts +7 -0
  140. package/dist/plugin/load-worker.d.ts.map +1 -0
  141. package/dist/plugin/load-worker.js +41 -0
  142. package/dist/plugin/load-worker.js.map +1 -0
  143. package/dist/plugin/load.d.ts +18 -0
  144. package/dist/plugin/load.d.ts.map +1 -0
  145. package/dist/plugin/load.js +62 -0
  146. package/dist/plugin/load.js.map +1 -0
  147. package/dist/plugin/manifest.d.ts +237 -0
  148. package/dist/plugin/manifest.d.ts.map +1 -0
  149. package/dist/plugin/manifest.js +411 -0
  150. package/dist/plugin/manifest.js.map +1 -0
  151. package/dist/plugin/refusals.d.ts +49 -0
  152. package/dist/plugin/refusals.d.ts.map +1 -0
  153. package/dist/plugin/refusals.js +79 -0
  154. package/dist/plugin/refusals.js.map +1 -0
  155. package/dist/plugin/rest-schemas.d.ts +556 -0
  156. package/dist/plugin/rest-schemas.d.ts.map +1 -0
  157. package/dist/plugin/rest-schemas.js +262 -0
  158. package/dist/plugin/rest-schemas.js.map +1 -0
  159. package/dist/plugin/sandbox.d.ts +23 -0
  160. package/dist/plugin/sandbox.d.ts.map +1 -0
  161. package/dist/plugin/sandbox.js +99 -0
  162. package/dist/plugin/sandbox.js.map +1 -0
  163. package/dist/plugin/testing.d.ts +45 -0
  164. package/dist/plugin/testing.d.ts.map +1 -0
  165. package/dist/plugin/testing.js +420 -0
  166. package/dist/plugin/testing.js.map +1 -0
  167. package/dist/plugin/web-react.d.ts +11 -0
  168. package/dist/plugin/web-react.d.ts.map +1 -0
  169. package/dist/plugin/web-react.js +34 -0
  170. package/dist/plugin/web-react.js.map +1 -0
  171. package/dist/plugin/web.d.ts +95 -0
  172. package/dist/plugin/web.d.ts.map +1 -0
  173. package/dist/plugin/web.js +245 -0
  174. package/dist/plugin/web.js.map +1 -0
  175. package/dist/plugin-nav.d.ts +29 -0
  176. package/dist/plugin-nav.d.ts.map +1 -0
  177. package/dist/plugin-nav.js +92 -0
  178. package/dist/plugin-nav.js.map +1 -0
  179. package/dist/plugin-testing.d.ts +2 -0
  180. package/dist/plugin-testing.d.ts.map +1 -0
  181. package/dist/plugin-testing.js +4 -0
  182. package/dist/plugin-testing.js.map +1 -0
  183. package/dist/plugin-web-react.d.ts +3 -0
  184. package/dist/plugin-web-react.d.ts.map +1 -0
  185. package/dist/plugin-web-react.js +3 -0
  186. package/dist/plugin-web-react.js.map +1 -0
  187. package/dist/plugin-web.d.ts +4 -0
  188. package/dist/plugin-web.d.ts.map +1 -0
  189. package/dist/plugin-web.js +5 -0
  190. package/dist/plugin-web.js.map +1 -0
  191. package/dist/plugin.d.ts +11 -0
  192. package/dist/plugin.d.ts.map +1 -0
  193. package/dist/plugin.js +15 -0
  194. package/dist/plugin.js.map +1 -0
  195. package/dist/router.d.ts +28 -0
  196. package/dist/router.d.ts.map +1 -1
  197. package/dist/router.js +47 -0
  198. package/dist/router.js.map +1 -1
  199. package/dist/routes.d.ts +6 -0
  200. package/dist/routes.d.ts.map +1 -1
  201. package/dist/routes.js +8 -0
  202. package/dist/routes.js.map +1 -1
  203. package/dist/schemas.d.ts +147 -1
  204. package/dist/schemas.d.ts.map +1 -1
  205. package/dist/schemas.js +55 -5
  206. package/dist/schemas.js.map +1 -1
  207. package/dist/tokens.d.ts +2 -0
  208. package/dist/tokens.d.ts.map +1 -0
  209. package/dist/tokens.js +101 -0
  210. package/dist/tokens.js.map +1 -0
  211. package/dist/vocabulary.d.ts +11 -1
  212. package/dist/vocabulary.d.ts.map +1 -1
  213. package/dist/vocabulary.js +24 -0
  214. package/dist/vocabulary.js.map +1 -1
  215. package/dist/web.d.ts.map +1 -1
  216. package/dist/web.js +5 -2
  217. package/dist/web.js.map +1 -1
  218. package/docs/specs/cli.md +16 -9
  219. package/docs/specs/plugins.md +487 -0
  220. package/docs/specs/rest.md +25 -13
  221. package/package.json +21 -3
  222. package/skills/building-a-console/SKILL.md +25 -26
  223. package/skills/building-a-console/references/configuring.md +40 -0
  224. package/skills/building-a-console/references/the-default-console.md +1 -1
  225. package/skills/building-a-console/references/the-look-from-a-website.md +5 -5
  226. package/skills/building-a-plugin/SKILL.md +137 -0
  227. package/skills/deploying-a-console/SKILL.md +24 -26
  228. package/skills/deploying-a-console/references/access.md +13 -9
  229. package/skills/deploying-a-console/references/resources.md +25 -10
  230. package/skills/deploying-a-console/references/running-it-again.md +15 -0
  231. package/skills/deploying-a-console/references/sizing.md +2 -0
  232. package/skills/planning-the-board/SKILL.md +96 -0
  233. package/skills/planning-the-board/references/a-task.md +36 -0
  234. package/skills/planning-the-board/references/setting-up.md +44 -0
  235. package/skills/working-the-board/SKILL.md +43 -34
  236. package/skills/working-the-board/references/the-pack.md +19 -0
  237. package/src/api.ts +16 -0
  238. package/src/cli/cli.ts +736 -45
  239. package/src/cli/format.ts +356 -9
  240. package/src/cli/help.ts +71 -6
  241. package/src/cli/plugin-check.ts +470 -0
  242. package/src/cli/plugin-cli.ts +148 -0
  243. package/src/cli/plugin-init.ts +90 -0
  244. package/src/cli/plugin-main.ts +30 -0
  245. package/src/client.ts +324 -1
  246. package/src/compat.ts +2 -2
  247. package/src/components/board.tsx +62 -14
  248. package/src/components/event-feed.tsx +42 -1
  249. package/src/components/markdown.tsx +6 -3
  250. package/src/components/pack-connect.tsx +10 -7
  251. package/src/components/pack-token.tsx +4 -1
  252. package/src/components/people.tsx +17 -7
  253. package/src/components/plugin-frame.tsx +437 -0
  254. package/src/components/plugin-settings.tsx +1111 -0
  255. package/src/components/remote-pictures.ts +19 -0
  256. package/src/components/shell.tsx +82 -14
  257. package/src/components/sign-in.tsx +114 -13
  258. package/src/components/ui.tsx +6 -3
  259. package/src/components/workspace.tsx +18 -0
  260. package/src/console.tsx +309 -23
  261. package/src/data.ts +47 -22
  262. package/src/i18n/en.ts +183 -1
  263. package/src/i18n/ko.ts +159 -1
  264. package/src/index.ts +12 -0
  265. package/src/limits.ts +14 -0
  266. package/src/plugin/args.ts +526 -0
  267. package/src/plugin/bridge.ts +314 -0
  268. package/src/plugin/capabilities.ts +150 -0
  269. package/src/plugin/context.ts +416 -0
  270. package/src/plugin/define.ts +792 -0
  271. package/src/plugin/definition-error.ts +19 -0
  272. package/src/plugin/limits.ts +70 -0
  273. package/src/plugin/load-worker.ts +50 -0
  274. package/src/plugin/load.ts +82 -0
  275. package/src/plugin/manifest.ts +561 -0
  276. package/src/plugin/refusals.ts +94 -0
  277. package/src/plugin/rest-schemas.ts +354 -0
  278. package/src/plugin/sandbox.ts +123 -0
  279. package/src/plugin/testing.ts +612 -0
  280. package/src/plugin/web-react.ts +45 -0
  281. package/src/plugin/web.ts +403 -0
  282. package/src/plugin-nav.ts +132 -0
  283. package/src/plugin-testing.ts +10 -0
  284. package/src/plugin-web-react.ts +4 -0
  285. package/src/plugin-web.ts +35 -0
  286. package/src/plugin.ts +126 -0
  287. package/src/router.ts +73 -0
  288. package/src/routes.ts +9 -0
  289. package/src/schemas.ts +60 -4
  290. package/src/styles/console.css +384 -5
  291. package/src/tokens.ts +101 -0
  292. package/src/vocabulary.ts +30 -0
  293. package/src/web.ts +5 -2
  294. package/template/plugin/.gitignore +2 -0
  295. package/template/plugin/README.md +26 -0
  296. package/template/plugin/SKILLCDN.md +13 -0
  297. package/template/plugin/build.mjs +40 -0
  298. package/template/plugin/console-plugin.json +50 -0
  299. package/template/plugin/package.json +27 -0
  300. package/template/plugin/skills/__key__/SKILL.md +23 -0
  301. package/template/plugin/src/check.test.ts +34 -0
  302. package/template/plugin/src/server/index.test.ts +41 -0
  303. package/template/plugin/src/server/index.ts +72 -0
  304. package/template/plugin/src/web/index.css +51 -0
  305. package/template/plugin/src/web/index.ts +115 -0
  306. package/template/plugin/tsconfig.json +17 -0
  307. package/skills/working-the-board/references/setting-up.md +0 -3
  308. /package/skills/{working-the-board → planning-the-board}/references/project-rules.md +0 -0
package/src/data.ts CHANGED
@@ -39,12 +39,14 @@ import {
39
39
  type RestPerson,
40
40
  type RestPersonPatch,
41
41
  type RestPersonWork,
42
+ type RestPlugin,
42
43
  type RestProject,
43
44
  type RestProjectInput,
44
45
  type RestProjectPatch,
45
46
  type RestProjectRules,
46
47
  type RestRules,
47
48
  type RestRulesInput,
49
+ type RestRulesPlugin,
48
50
  type RestRun,
49
51
  type RestSkills,
50
52
  type RestTask,
@@ -66,12 +68,12 @@ import {
66
68
 
67
69
  // What the console shows, and how it stays current: the workspace (who is signed in, the
68
70
  // projects, the people, the tokens) is loaded once; the project the page is on is loaded whole,
69
- // its feed from the beginning, and from then on the project's stream says when something
71
+ // its feed as it stands, the newest page, and from then on the project's stream says when something
70
72
  // changed: on every event the lists are loaded again, which is the boring way to be right.
71
73
 
72
74
  /** How many events the feed keeps on the page. */
73
75
  const FEED_KEEP = 200;
74
- /** How many pages of the feed are read at the start before the stream takes over. */
76
+ /** How many pages of a task's own events are read for its thread, which is read from the beginning. */
75
77
  const FEED_MAX_PAGES = 20;
76
78
  /** How long changes that arrive together are gathered before the lists are loaded once. */
77
79
  const RELOAD_DELAY_MS = 150;
@@ -89,8 +91,10 @@ export interface ConsoleActions {
89
91
  removePackToken(): Promise<RestProject>;
90
92
  /** Asks the deployment to connect the project's pack with a code a person approves (ADR-0036); for an owner. */
91
93
  connectPackToken(): Promise<RestPackConnectStarted>;
92
- /** How that connection stands; once connected, the project is the one answered. */
94
+ /** How that connection stands, read and changed nothing. */
93
95
  packTokenConnection(signal?: AbortSignal): Promise<RestPackConnect>;
96
+ /** Asks the console to claim the token at the deployment (ADR-0046); once connected, the project is the one answered. */
97
+ claimPackTokenConnection(signal?: AbortSignal): Promise<RestPackConnect>;
94
98
  cancelPackTokenConnection(): Promise<void>;
95
99
  addMember(input: RestMemberInput): Promise<RestMember>;
96
100
  updateMember(personId: string, patch: RestMemberPatch): Promise<RestMember>;
@@ -177,6 +181,7 @@ export interface ConsoleActions {
177
181
  removeWorkspacePackToken(): Promise<RestWorkspace>;
178
182
  connectWorkspacePackToken(): Promise<RestPackConnectStarted>;
179
183
  workspacePackTokenConnection(signal?: AbortSignal): Promise<RestWorkspacePackConnect>;
184
+ claimWorkspacePackTokenConnection(signal?: AbortSignal): Promise<RestWorkspacePackConnect>;
180
185
  cancelWorkspacePackTokenConnection(): Promise<void>;
181
186
  /** One person of the workspace by their exact login, or nobody: what an owner lists someone the People page does not show by. */
182
187
  findPerson(login: string, signal?: AbortSignal): Promise<RestPerson | undefined>;
@@ -195,6 +200,8 @@ export interface ConsoleActions {
195
200
  connectRequest(code: string, signal?: AbortSignal): Promise<RestConnectRequest>;
196
201
  approveConnection(code: string, input: RestTokenInput): Promise<RestConnectRequest>;
197
202
  denyConnection(code: string): Promise<void>;
203
+ /** Reads the plugins again: after one was enabled, configured or disabled on the settings. */
204
+ refreshPlugins(): Promise<void>;
198
205
  /** Another person's plate: for an administrator, or an owner of a project they are in. */
199
206
  personWork(personId: string, signal?: AbortSignal): Promise<RestPersonWork>;
200
207
  /** The tokens of another person, and the way to take one away: for an administrator. */
@@ -220,6 +227,8 @@ export interface ConsoleData {
220
227
  readonly organization: RestOrganization | null;
221
228
  /** The categories tasks are sorted into, by name. */
222
229
  readonly categories: readonly RestCategory[];
230
+ /** The plugins the person may know of (docs/specs/plugins.md), by key: the enabled ones, every one for an administrator. */
231
+ readonly plugins: readonly RestPlugin[];
223
232
  /** The tokens of whoever is signed in, newest first. */
224
233
  readonly tokens: readonly RestToken[];
225
234
  /** The project the page is on, once loaded; `undefined` while loading, or when the page is on none. */
@@ -242,28 +251,25 @@ export interface ConsoleData {
242
251
  readonly members: readonly RestMember[];
243
252
  /** The project's skills, as the console answered; `undefined` until it has. */
244
253
  readonly skills: RestSkills | undefined;
254
+ /** The plugins bound to the project that the person holds a role in, as the rules name them. */
255
+ readonly projectPlugins: readonly RestRulesPlugin[];
245
256
  readonly actions: ConsoleActions;
246
257
  /** Asks everything again, from who is signed in on. */
247
258
  reload(): void;
248
259
  }
249
260
 
250
- /** The whole feed of a project from the beginning, page by page, bounded. */
261
+ /**
262
+ * The feed of a project as it stands: its newest page, oldest first, from which the stream
263
+ * goes on. Read in one request, where reading it from the beginning was a round trip per
264
+ * hundred events, each waited for before the board was shown.
265
+ */
251
266
  async function readFeed(
252
267
  client: ConsoleClient,
253
268
  key: string,
254
269
  signal: AbortSignal,
255
270
  ): Promise<RestEvent[]> {
256
- const events: RestEvent[] = [];
257
- let after = 0;
258
- for (let page = 0; page < FEED_MAX_PAGES; page += 1) {
259
- const result = await client.project(key).events(after, {}, signal);
260
- events.push(...result.items);
261
- after = result.items.at(-1)?.id ?? after;
262
- if (!result.more) {
263
- break;
264
- }
265
- }
266
- return events.slice(-FEED_KEEP);
271
+ const result = await client.project(key).events(0, { latest: true }, signal);
272
+ return result.items.slice(-FEED_KEEP);
267
273
  }
268
274
 
269
275
  /** How many pages of the current tasks are read before the board is shown as it stands. */
@@ -356,6 +362,7 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
356
362
  const [listed, setListed] = useState<readonly RestListing[]>([]);
357
363
  const [organization, setOrganization] = useState<RestOrganization | null>(null);
358
364
  const [categories, setCategories] = useState<readonly RestCategory[]>([]);
365
+ const [plugins, setPlugins] = useState<readonly RestPlugin[]>([]);
359
366
  const [tokens, setTokens] = useState<readonly RestToken[]>([]);
360
367
  const [project, setProject] = useState<RestProject | undefined>(undefined);
361
368
  const [projectLoaded, setProjectLoaded] = useState(false);
@@ -368,6 +375,7 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
368
375
  const [events, setEvents] = useState<readonly RestEvent[]>([]);
369
376
  const [members, setMembers] = useState<readonly RestMember[]>([]);
370
377
  const [skills, setSkills] = useState<RestSkills | undefined>(undefined);
378
+ const [projectPlugins, setProjectPlugins] = useState<readonly RestRulesPlugin[]>([]);
371
379
  const [generation, setGeneration] = useState(0);
372
380
  const lastEvent = useRef(0);
373
381
  const signedIn = me?.person !== null && me !== undefined;
@@ -377,10 +385,12 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
377
385
  /** Loads the workspace's lists again. What fails stays as it was. */
378
386
  const refreshWorkspace = useCallback(
379
387
  async (signal?: AbortSignal) => {
380
- const [nextProjects, nextPeople, nextCategories] = await Promise.all([
388
+ const [nextProjects, nextPeople, nextCategories, nextPlugins] = await Promise.all([
381
389
  client.projects(signal),
382
390
  client.people(signal),
383
391
  client.categories(signal),
392
+ // A console without plugins answers none; one older than them answers nothing, which is none too.
393
+ client.plugins(signal).catch(() => ({ items: [], sql: false })),
384
394
  ]);
385
395
  if (signal?.aborted === true) {
386
396
  return;
@@ -390,6 +400,7 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
390
400
  setListed(nextPeople.listed);
391
401
  setOrganization(nextPeople.organization);
392
402
  setCategories(nextCategories.items);
403
+ setPlugins(nextPlugins.items);
393
404
  },
394
405
  [client],
395
406
  );
@@ -401,7 +412,7 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
401
412
  return;
402
413
  }
403
414
  const scope = client.project(projectKey);
404
- const [nextProject, nextTasks, nextDecisions, nextGoals, nextRuns, nextMembers] =
415
+ const [nextProject, nextTasks, nextDecisions, nextGoals, nextRuns, nextMembers, nextPlugins] =
405
416
  await Promise.all([
406
417
  scope.get(signal),
407
418
  readTasks(scope, signal),
@@ -409,6 +420,11 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
409
420
  readGoals(scope, signal),
410
421
  scope.runs(undefined, signal),
411
422
  scope.members(signal),
423
+ // The plugins bound to the project come with its rules; a console older than them names none.
424
+ scope
425
+ .rules(signal)
426
+ .then((rules) => rules.plugins ?? [])
427
+ .catch((): readonly RestRulesPlugin[] => []),
412
428
  ]);
413
429
  if (signal?.aborted === true) {
414
430
  return;
@@ -419,6 +435,7 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
419
435
  setGoals(nextGoals.items);
420
436
  setRuns(nextRuns.items);
421
437
  setMembers(nextMembers.items);
438
+ setProjectPlugins(nextPlugins);
422
439
  },
423
440
  [client, projectKey],
424
441
  );
@@ -468,7 +485,7 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
468
485
  return () => controller.abort();
469
486
  }, [client, generation, refreshWorkspace]);
470
487
 
471
- // The project the page is on: its lists, its feed from the beginning, and its stream from
488
+ // The project the page is on: its lists, its feed as it stands, the newest page, and its stream from
472
489
  // where the feed ends. Another project, or none, starts over.
473
490
  // biome-ignore lint/correctness/useExhaustiveDependencies: `generation` is the trigger
474
491
  useEffect(() => {
@@ -482,6 +499,7 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
482
499
  setEvents([]);
483
500
  setMembers([]);
484
501
  setSkills(undefined);
502
+ setProjectPlugins([]);
485
503
  setLive(false);
486
504
  if (projectKey === undefined || !signedIn) {
487
505
  return;
@@ -612,8 +630,9 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
612
630
  return changed;
613
631
  },
614
632
  connectPackToken: () => scope().connectPackToken(),
615
- async packTokenConnection(signal) {
616
- const state = await scope().packTokenConnection(signal);
633
+ packTokenConnection: (signal) => scope().packTokenConnection(signal),
634
+ async claimPackTokenConnection(signal) {
635
+ const state = await scope().claimPackTokenConnection(signal);
617
636
  if (state.status === "connected") {
618
637
  setProject(state.project);
619
638
  }
@@ -756,8 +775,9 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
756
775
  return workspace;
757
776
  },
758
777
  connectWorkspacePackToken: () => client.connectWorkspacePackToken(),
759
- async workspacePackTokenConnection(signal) {
760
- const state = await client.workspacePackTokenConnection(signal);
778
+ workspacePackTokenConnection: (signal) => client.workspacePackTokenConnection(signal),
779
+ async claimWorkspacePackTokenConnection(signal) {
780
+ const state = await client.claimWorkspacePackTokenConnection(signal);
761
781
  if (state.status === "connected") {
762
782
  const { workspace } = state;
763
783
  setMe((current) => (current === undefined ? undefined : { ...current, workspace }));
@@ -766,6 +786,9 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
766
786
  },
767
787
  cancelWorkspacePackTokenConnection: () => client.cancelWorkspacePackTokenConnection(),
768
788
  findPerson: (login, signal) => client.findPerson(login, signal),
789
+ async refreshPlugins() {
790
+ setPlugins((await client.plugins()).items);
791
+ },
769
792
  async abandonRun(id) {
770
793
  const run = await scope().endRun(id, { status: "abandoned" });
771
794
  await refreshProject();
@@ -822,6 +845,7 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
822
845
  listed,
823
846
  organization,
824
847
  categories,
848
+ plugins,
825
849
  tokens,
826
850
  project,
827
851
  projectLoaded,
@@ -834,6 +858,7 @@ export function useConsoleData(client: ConsoleClient, projectKey: string | undef
834
858
  events,
835
859
  members,
836
860
  skills,
861
+ projectPlugins,
837
862
  actions,
838
863
  reload,
839
864
  };
package/src/i18n/en.ts CHANGED
@@ -29,6 +29,15 @@ export const en = {
29
29
  label: "English",
30
30
 
31
31
  vocabulary: {
32
+ /** The states of a plugin (docs/specs/plugins.md, "Lifecycle"). */
33
+ pluginState: {
34
+ installed: "Installed",
35
+ enabled: "Enabled",
36
+ needs_consent: "Needs consent",
37
+ disabled: "Disabled",
38
+ failed: "Failed",
39
+ orphaned: "Orphaned",
40
+ } as Record<string, string>,
32
41
  state: {
33
42
  idea: "Idea",
34
43
  ready: "Ready",
@@ -143,6 +152,138 @@ export const en = {
143
152
  skills: "Skills",
144
153
  members: "Members",
145
154
  settings: "Settings",
155
+ plugins: "Plugins",
156
+ /** The groups of tabs the console knows (docs/specs/plugins.md, "Tabs, sub-tabs and addresses"). */
157
+ pluginGroups: {
158
+ people: "People",
159
+ customers: "Customers",
160
+ money: "Money",
161
+ operations: "Operations",
162
+ marketing: "Marketing",
163
+ engineering: "Engineering",
164
+ reports: "Reports",
165
+ } as Record<string, string>,
166
+ },
167
+
168
+ plugins: {
169
+ frame: (name: string) => `${name}, a plugin's page`,
170
+ noPage: {
171
+ title: "Nothing here",
172
+ body: "This plugin has no page you may see, or it is not on this project.",
173
+ },
174
+ settings: {
175
+ title: "Plugins",
176
+ empty: { title: "No plugins", body: "None is installed in this console's image." },
177
+ none: "The console has no plugins directory: none can be installed.",
178
+ about: "About",
179
+ problems: "Why it cannot run",
180
+ nothing: "None",
181
+ capabilities: "Capabilities",
182
+ board: "Board project",
183
+ boardHint: "Where its decisions, tasks and pages go.",
184
+ boardNone: "None",
185
+ boardCreate: "A new project",
186
+ boardChoose: "An existing project",
187
+ boardKey: "Key",
188
+ boardName: "Name",
189
+ boardDescription: "Description",
190
+ projects: "Projects",
191
+ projectsAll: "Every project",
192
+ projectsSome: "Some projects",
193
+ settingsForm: "Settings",
194
+ secretSet: (when: string) => `Set ${when}`,
195
+ secretUnset: "Not set",
196
+ secretNew: "New value",
197
+ enable: "Enable",
198
+ disable: "Disable",
199
+ roles: "Roles",
200
+ rolesNone: "No roles: every member may use it.",
201
+ roleDefault: "Default",
202
+ everyMember: "Every member",
203
+ grantEveryone: "Everyone",
204
+ grantMembersOf: (project: string) => `Members of ${project}`,
205
+ grantPeople: "People",
206
+ grantProjects: "Members of a project",
207
+ grantWhom: "Grant to…",
208
+ grant: "Grant",
209
+ revoke: "Revoke",
210
+ events: "What happened",
211
+ noEvents: "Nothing yet.",
212
+ data: "Data",
213
+ export: "Export",
214
+ exportHint: "Its records and their versions, as JSON Lines.",
215
+ exportLink: "Download",
216
+ purge: "Purge",
217
+ purgeHint:
218
+ "Removes its records, files, grants and secrets from the console. What it put on the board stays.",
219
+ purgeDisabledFirst: "Disable it first.",
220
+ purgeConfirm: (key: string) => `Type ${key}`,
221
+ purged: "Purged.",
222
+ agentNote: "For agents",
223
+ operations: "Commands and queries",
224
+ },
225
+ /** The capabilities (docs/specs/plugins.md, "Capabilities"), each by a name and the sentence of what it opens, as an administrator reads them before consenting. */
226
+ capability: {
227
+ files: {
228
+ name: "Files",
229
+ opens: "Keeps files on its records, within the bytes the deployment allows.",
230
+ },
231
+ "people:read": { name: "People", opens: "Reads the people the person may see." },
232
+ "people:read:all": {
233
+ name: "Everyone",
234
+ opens: "Reads everyone in the workspace, from its jobs and hooks too.",
235
+ },
236
+ "projects:read": {
237
+ name: "Projects",
238
+ opens: "Reads the projects and members the person may see.",
239
+ },
240
+ "projects:read:all": {
241
+ name: "Every project",
242
+ opens: "Reads every project and its members.",
243
+ },
244
+ "board:read": {
245
+ name: "Board",
246
+ opens:
247
+ "Reads the tasks, decisions, runs, pages and events of its board projects, and of the project a call is on.",
248
+ },
249
+ "board:tasks": {
250
+ name: "Tasks",
251
+ opens: "Creates tasks in its projects; changes, closes and comments on its own.",
252
+ },
253
+ "board:decisions": {
254
+ name: "Decisions",
255
+ opens: "Raises decisions in its projects and reads the answers to its own.",
256
+ },
257
+ "board:documents": {
258
+ name: "Pages",
259
+ opens: "Writes pages in a folder of its own in its projects.",
260
+ },
261
+ "events:console": {
262
+ name: "Console events",
263
+ opens: "Runs hooks on what happens in its board projects.",
264
+ },
265
+ "events:plugins": {
266
+ name: "Other plugins' events",
267
+ opens: "Runs hooks on the events other plugins emit.",
268
+ },
269
+ jobs: { name: "Schedule", opens: "Runs at the times it names." },
270
+ endpoints: {
271
+ name: "Inbound requests",
272
+ opens: "Takes requests under its own path, a person's or a webhook's.",
273
+ },
274
+ "storage:sql": {
275
+ name: "Own database schema",
276
+ opens: "Gets a schema and a login role of its own in the console's database.",
277
+ },
278
+ outbound: (host: string) => ({
279
+ name: `Requests to ${host}`,
280
+ opens: `Sends https requests to ${host}.`,
281
+ }),
282
+ plugins: (key: string) => ({
283
+ name: `Plugin ${key}`,
284
+ opens: `Calls the API of the plugin ${key}, as the person.`,
285
+ }),
286
+ },
146
287
  },
147
288
 
148
289
  shell: {
@@ -157,7 +298,15 @@ export const en = {
157
298
 
158
299
  signIn: {
159
300
  lead: "Join an organization made of AI agents.",
160
- continueWith: (provider: string) => `Continue with ${provider}`,
301
+ signInWith: (provider: string) => `Sign in with ${provider}`,
302
+ /** The button while the browser is on its way to the provider. */
303
+ going: (provider: string) => `Going to ${provider}…`,
304
+ /** The line under a button: who gets in through that way. */
305
+ admits: {
306
+ organization: (name: string) => `For the members of ${name}, the organization at GitHub.`,
307
+ domains: (domains: string) => `For accounts at ${domains}.`,
308
+ listed: "For the people an administrator added.",
309
+ },
161
310
  nobody: {
162
311
  title: "Nobody can sign in here yet",
163
312
  body: "Signing in is not configured on this console. Whoever runs it sets it up as deploy/README.md describes.",
@@ -176,6 +325,8 @@ export const en = {
176
325
  empty: {
177
326
  title: "No projects yet",
178
327
  body: "Make the first one: what it is for, and who is in it. Or ask an owner to add you to theirs.",
328
+ bodyMember:
329
+ "An administrator makes the projects. Ask one for the first, or an owner to add you to theirs.",
179
330
  },
180
331
  list: "Projects",
181
332
  private: "Private",
@@ -585,6 +736,8 @@ export const en = {
585
736
  workspace: {
586
737
  title: "Workspace",
587
738
  aria: "Workspace settings",
739
+ consoleVersion: "Console version",
740
+ consoleTakes: (range: string) => `Takes the console command ${range}`,
588
741
  peopleVisibility: "Member visibility",
589
742
  peopleVisibilityHint:
590
743
  "Whom a member sees on the People page and in the pickers: everyone of the workspace, or only the people they share a project with. An administrator always sees everyone, and a project open to the workspace has everyone in it.",
@@ -630,6 +783,17 @@ export const en = {
630
783
  as: (agent: string) => `as ${agent}`,
631
784
  asTitle: "The agent the person acted through",
632
785
  aTask: "a task",
786
+ aPlugin: "a plugin",
787
+ /** After the sentence of a board's event a plugin caused (ADR-0052). */
788
+ throughPlugin: (plugin: string) => `through the plugin ${plugin}`,
789
+ everyone: "everyone",
790
+ membersOf: (project: string) => `the members of ${project}`,
791
+ outcome: {
792
+ ok: "done",
793
+ refused: "refused",
794
+ waiting: "waiting for a decision",
795
+ failed: "failed",
796
+ } as Readonly<Record<string, string>>,
633
797
  aProject: "a project",
634
798
  someone: "someone",
635
799
  something: "something",
@@ -715,6 +879,20 @@ export const en = {
715
879
  documentArchived: (page: string) => `archived ${page}`,
716
880
  documentRestored: (page: string) => `restored ${page}`,
717
881
  fileAttached: (label: string, page: string) => `attached ${label} to ${page}`,
882
+ pluginEnabled: (plugin: string) => `enabled the plugin ${plugin}`,
883
+ pluginDisabled: (plugin: string) => `disabled the plugin ${plugin}`,
884
+ pluginConfigured: (fields: string, plugin: string) =>
885
+ `changed ${fields} of the plugin ${plugin}`,
886
+ pluginGranted: (role: string, who: string, plugin: string) =>
887
+ `gave ${who} the role ${role} in the plugin ${plugin}`,
888
+ pluginRevoked: (role: string, who: string, plugin: string) =>
889
+ `took the role ${role} from ${who} in the plugin ${plugin}`,
890
+ pluginUpgraded: (plugin: string, version: string) =>
891
+ `brought the plugin ${plugin} to ${version}`,
892
+ pluginCommand: (command: string, plugin: string, outcome: string) =>
893
+ `ran ${command} of the plugin ${plugin}: ${outcome}`,
894
+ pluginPurged: (plugin: string) => `purged the plugin ${plugin}: everything it kept is gone`,
895
+ pluginFailed: (plugin: string, problem: string) => `the plugin ${plugin} failed: ${problem}`,
718
896
  },
719
897
  },
720
898
 
@@ -889,6 +1067,10 @@ export const en = {
889
1067
  domain: "Of the domain",
890
1068
  listed: "Added by an administrator",
891
1069
  } satisfies Record<MembershipSource, string>,
1070
+ /** A person the organization let in, by the organization's name: never read as their own. */
1071
+ ofOrganization: (name: string) => `Member of ${name}`,
1072
+ /** A person a Workspace domain let in, by the domain of their login. */
1073
+ ofDomain: (domain: string) => `Account at ${domain}`,
892
1074
  at: { gh: "at GitHub", google: "at Google" } satisfies Record<ProviderKey, string>,
893
1075
  organization: (name: string) => `The organization ${name} at GitHub says who is in.`,
894
1076
  organizationRead: "Last read",