@skillcdn/console 0.4.1 → 0.4.3

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 (299) hide show
  1. package/README.md +19 -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 +569 -48
  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 +283 -7
  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 +44 -7
  16. package/dist/cli/help.js.map +1 -1
  17. package/dist/cli/plugin-check.d.ts +26 -0
  18. package/dist/cli/plugin-check.d.ts.map +1 -0
  19. package/dist/cli/plugin-check.js +396 -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 +189 -0
  24. package/dist/cli/plugin-cli.js.map +1 -0
  25. package/dist/cli/plugin-init.d.ts +37 -0
  26. package/dist/cli/plugin-init.d.ts.map +1 -0
  27. package/dist/cli/plugin-init.js +206 -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 +100 -3
  34. package/dist/client.d.ts.map +1 -1
  35. package/dist/client.js +79 -7
  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/comments.d.ts.map +1 -1
  44. package/dist/components/comments.js +1 -1
  45. package/dist/components/comments.js.map +1 -1
  46. package/dist/components/decision-form.d.ts +2 -1
  47. package/dist/components/decision-form.d.ts.map +1 -1
  48. package/dist/components/decision-form.js +36 -4
  49. package/dist/components/decision-form.js.map +1 -1
  50. package/dist/components/decision-list.d.ts +4 -0
  51. package/dist/components/decision-list.d.ts.map +1 -1
  52. package/dist/components/decision-list.js +29 -3
  53. package/dist/components/decision-list.js.map +1 -1
  54. package/dist/components/event-feed.d.ts +1 -1
  55. package/dist/components/event-feed.d.ts.map +1 -1
  56. package/dist/components/event-feed.js +33 -1
  57. package/dist/components/event-feed.js.map +1 -1
  58. package/dist/components/my-decisions.d.ts +2 -0
  59. package/dist/components/my-decisions.d.ts.map +1 -1
  60. package/dist/components/my-decisions.js +2 -2
  61. package/dist/components/my-decisions.js.map +1 -1
  62. package/dist/components/plugin-frame.d.ts +52 -0
  63. package/dist/components/plugin-frame.d.ts.map +1 -0
  64. package/dist/components/plugin-frame.js +314 -0
  65. package/dist/components/plugin-frame.js.map +1 -0
  66. package/dist/components/plugin-settings.d.ts +38 -0
  67. package/dist/components/plugin-settings.d.ts.map +1 -0
  68. package/dist/components/plugin-settings.js +348 -0
  69. package/dist/components/plugin-settings.js.map +1 -0
  70. package/dist/components/shell.d.ts +2 -0
  71. package/dist/components/shell.d.ts.map +1 -1
  72. package/dist/components/shell.js +26 -1
  73. package/dist/components/shell.js.map +1 -1
  74. package/dist/components/task-view.d.ts +2 -1
  75. package/dist/components/task-view.d.ts.map +1 -1
  76. package/dist/components/task-view.js.map +1 -1
  77. package/dist/components/workspace.d.ts +7 -1
  78. package/dist/components/workspace.d.ts.map +1 -1
  79. package/dist/components/workspace.js +1 -1
  80. package/dist/components/workspace.js.map +1 -1
  81. package/dist/console.css +330 -1
  82. package/dist/console.d.ts.map +1 -1
  83. package/dist/console.js +152 -41
  84. package/dist/console.js.map +1 -1
  85. package/dist/data.d.ts +9 -2
  86. package/dist/data.d.ts.map +1 -1
  87. package/dist/data.js +45 -9
  88. package/dist/data.js.map +1 -1
  89. package/dist/i18n/en.d.ts +155 -0
  90. package/dist/i18n/en.d.ts.map +1 -1
  91. package/dist/i18n/en.js +166 -0
  92. package/dist/i18n/en.js.map +1 -1
  93. package/dist/i18n/ko.d.ts.map +1 -1
  94. package/dist/i18n/ko.js +150 -0
  95. package/dist/i18n/ko.js.map +1 -1
  96. package/dist/index.d.ts +3 -1
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +3 -1
  99. package/dist/index.js.map +1 -1
  100. package/dist/limits.d.ts +2 -0
  101. package/dist/limits.d.ts.map +1 -1
  102. package/dist/limits.js +2 -0
  103. package/dist/limits.js.map +1 -1
  104. package/dist/plugin/args.d.ts +148 -0
  105. package/dist/plugin/args.d.ts.map +1 -0
  106. package/dist/plugin/args.js +369 -0
  107. package/dist/plugin/args.js.map +1 -0
  108. package/dist/plugin/bridge.d.ts +146 -0
  109. package/dist/plugin/bridge.d.ts.map +1 -0
  110. package/dist/plugin/bridge.js +125 -0
  111. package/dist/plugin/bridge.js.map +1 -0
  112. package/dist/plugin/capabilities.d.ts +20 -0
  113. package/dist/plugin/capabilities.d.ts.map +1 -0
  114. package/dist/plugin/capabilities.js +133 -0
  115. package/dist/plugin/capabilities.js.map +1 -0
  116. package/dist/plugin/context.d.ts +380 -0
  117. package/dist/plugin/context.d.ts.map +1 -0
  118. package/dist/plugin/context.js +2 -0
  119. package/dist/plugin/context.js.map +1 -0
  120. package/dist/plugin/define.d.ts +208 -0
  121. package/dist/plugin/define.d.ts.map +1 -0
  122. package/dist/plugin/define.js +482 -0
  123. package/dist/plugin/define.js.map +1 -0
  124. package/dist/plugin/definition-error.d.ts +11 -0
  125. package/dist/plugin/definition-error.d.ts.map +1 -0
  126. package/dist/plugin/definition-error.js +17 -0
  127. package/dist/plugin/definition-error.js.map +1 -0
  128. package/dist/plugin/limits.d.ts +67 -0
  129. package/dist/plugin/limits.d.ts.map +1 -0
  130. package/dist/plugin/limits.js +70 -0
  131. package/dist/plugin/limits.js.map +1 -0
  132. package/dist/plugin/load-worker.d.ts +7 -0
  133. package/dist/plugin/load-worker.d.ts.map +1 -0
  134. package/dist/plugin/load-worker.js +41 -0
  135. package/dist/plugin/load-worker.js.map +1 -0
  136. package/dist/plugin/load.d.ts +18 -0
  137. package/dist/plugin/load.d.ts.map +1 -0
  138. package/dist/plugin/load.js +62 -0
  139. package/dist/plugin/load.js.map +1 -0
  140. package/dist/plugin/manifest.d.ts +237 -0
  141. package/dist/plugin/manifest.d.ts.map +1 -0
  142. package/dist/plugin/manifest.js +411 -0
  143. package/dist/plugin/manifest.js.map +1 -0
  144. package/dist/plugin/refusals.d.ts +49 -0
  145. package/dist/plugin/refusals.d.ts.map +1 -0
  146. package/dist/plugin/refusals.js +79 -0
  147. package/dist/plugin/refusals.js.map +1 -0
  148. package/dist/plugin/rest-schemas.d.ts +556 -0
  149. package/dist/plugin/rest-schemas.d.ts.map +1 -0
  150. package/dist/plugin/rest-schemas.js +262 -0
  151. package/dist/plugin/rest-schemas.js.map +1 -0
  152. package/dist/plugin/sandbox.d.ts +23 -0
  153. package/dist/plugin/sandbox.d.ts.map +1 -0
  154. package/dist/plugin/sandbox.js +99 -0
  155. package/dist/plugin/sandbox.js.map +1 -0
  156. package/dist/plugin/testing.d.ts +45 -0
  157. package/dist/plugin/testing.d.ts.map +1 -0
  158. package/dist/plugin/testing.js +420 -0
  159. package/dist/plugin/testing.js.map +1 -0
  160. package/dist/plugin/web-react.d.ts +11 -0
  161. package/dist/plugin/web-react.d.ts.map +1 -0
  162. package/dist/plugin/web-react.js +34 -0
  163. package/dist/plugin/web-react.js.map +1 -0
  164. package/dist/plugin/web.d.ts +95 -0
  165. package/dist/plugin/web.d.ts.map +1 -0
  166. package/dist/plugin/web.js +245 -0
  167. package/dist/plugin/web.js.map +1 -0
  168. package/dist/plugin-nav.d.ts +29 -0
  169. package/dist/plugin-nav.d.ts.map +1 -0
  170. package/dist/plugin-nav.js +92 -0
  171. package/dist/plugin-nav.js.map +1 -0
  172. package/dist/plugin-testing.d.ts +2 -0
  173. package/dist/plugin-testing.d.ts.map +1 -0
  174. package/dist/plugin-testing.js +4 -0
  175. package/dist/plugin-testing.js.map +1 -0
  176. package/dist/plugin-web-react.d.ts +3 -0
  177. package/dist/plugin-web-react.d.ts.map +1 -0
  178. package/dist/plugin-web-react.js +3 -0
  179. package/dist/plugin-web-react.js.map +1 -0
  180. package/dist/plugin-web.d.ts +4 -0
  181. package/dist/plugin-web.d.ts.map +1 -0
  182. package/dist/plugin-web.js +5 -0
  183. package/dist/plugin-web.js.map +1 -0
  184. package/dist/plugin.d.ts +11 -0
  185. package/dist/plugin.d.ts.map +1 -0
  186. package/dist/plugin.js +15 -0
  187. package/dist/plugin.js.map +1 -0
  188. package/dist/router.d.ts +28 -0
  189. package/dist/router.d.ts.map +1 -1
  190. package/dist/router.js +47 -0
  191. package/dist/router.js.map +1 -1
  192. package/dist/routes.d.ts +4 -0
  193. package/dist/routes.d.ts.map +1 -1
  194. package/dist/routes.js +6 -0
  195. package/dist/routes.js.map +1 -1
  196. package/dist/schemas.d.ts +194 -9
  197. package/dist/schemas.d.ts.map +1 -1
  198. package/dist/schemas.js +98 -17
  199. package/dist/schemas.js.map +1 -1
  200. package/dist/tokens.d.ts +2 -0
  201. package/dist/tokens.d.ts.map +1 -0
  202. package/dist/tokens.js +101 -0
  203. package/dist/tokens.js.map +1 -0
  204. package/dist/vocabulary.d.ts +11 -1
  205. package/dist/vocabulary.d.ts.map +1 -1
  206. package/dist/vocabulary.js +24 -0
  207. package/dist/vocabulary.js.map +1 -1
  208. package/dist/web.d.ts.map +1 -1
  209. package/dist/web.js +2 -0
  210. package/dist/web.js.map +1 -1
  211. package/docs/specs/cli.md +12 -5
  212. package/docs/specs/plugins.md +488 -0
  213. package/docs/specs/rest.md +11 -9
  214. package/package.json +26 -8
  215. package/skills/building-a-console/SKILL.md +25 -26
  216. package/skills/building-a-console/references/configuring.md +40 -0
  217. package/skills/building-a-console/references/the-default-console.md +1 -1
  218. package/skills/building-a-console/references/the-look-from-a-website.md +5 -5
  219. package/skills/building-a-plugin/SKILL.md +137 -0
  220. package/skills/deploying-a-console/SKILL.md +24 -26
  221. package/skills/deploying-a-console/references/access.md +13 -9
  222. package/skills/deploying-a-console/references/resources.md +25 -10
  223. package/skills/deploying-a-console/references/running-it-again.md +15 -0
  224. package/skills/deploying-a-console/references/sizing.md +2 -0
  225. package/skills/planning-the-board/SKILL.md +96 -0
  226. package/skills/planning-the-board/references/a-task.md +36 -0
  227. package/skills/planning-the-board/references/setting-up.md +44 -0
  228. package/skills/working-the-board/SKILL.md +43 -34
  229. package/skills/working-the-board/references/the-pack.md +19 -0
  230. package/src/api.ts +12 -0
  231. package/src/cli/cli.ts +737 -55
  232. package/src/cli/format.ts +368 -10
  233. package/src/cli/help.ts +50 -6
  234. package/src/cli/plugin-check.ts +497 -0
  235. package/src/cli/plugin-cli.ts +237 -0
  236. package/src/cli/plugin-init.ts +257 -0
  237. package/src/cli/plugin-main.ts +30 -0
  238. package/src/client.ts +322 -9
  239. package/src/compat.ts +2 -2
  240. package/src/components/board.tsx +62 -14
  241. package/src/components/comments.tsx +1 -0
  242. package/src/components/decision-form.tsx +92 -11
  243. package/src/components/decision-list.tsx +71 -2
  244. package/src/components/event-feed.tsx +42 -1
  245. package/src/components/my-decisions.tsx +4 -1
  246. package/src/components/plugin-frame.tsx +437 -0
  247. package/src/components/plugin-settings.tsx +1111 -0
  248. package/src/components/shell.tsx +82 -14
  249. package/src/components/task-view.tsx +2 -1
  250. package/src/components/workspace.tsx +18 -0
  251. package/src/console.tsx +304 -31
  252. package/src/data.ts +59 -11
  253. package/src/i18n/en.ts +175 -0
  254. package/src/i18n/ko.ts +155 -0
  255. package/src/index.ts +12 -0
  256. package/src/limits.ts +2 -0
  257. package/src/plugin/args.ts +526 -0
  258. package/src/plugin/bridge.ts +314 -0
  259. package/src/plugin/capabilities.ts +150 -0
  260. package/src/plugin/context.ts +421 -0
  261. package/src/plugin/define.ts +792 -0
  262. package/src/plugin/definition-error.ts +19 -0
  263. package/src/plugin/limits.ts +70 -0
  264. package/src/plugin/load-worker.ts +50 -0
  265. package/src/plugin/load.ts +82 -0
  266. package/src/plugin/manifest.ts +561 -0
  267. package/src/plugin/refusals.ts +94 -0
  268. package/src/plugin/rest-schemas.ts +354 -0
  269. package/src/plugin/sandbox.ts +123 -0
  270. package/src/plugin/testing.ts +612 -0
  271. package/src/plugin/web-react.ts +45 -0
  272. package/src/plugin/web.ts +403 -0
  273. package/src/plugin-nav.ts +132 -0
  274. package/src/plugin-testing.ts +10 -0
  275. package/src/plugin-web-react.ts +4 -0
  276. package/src/plugin-web.ts +35 -0
  277. package/src/plugin.ts +126 -0
  278. package/src/router.ts +73 -0
  279. package/src/routes.ts +7 -0
  280. package/src/schemas.ts +115 -18
  281. package/src/styles/console.css +330 -1
  282. package/src/tokens.ts +101 -0
  283. package/src/vocabulary.ts +30 -0
  284. package/src/web.ts +2 -0
  285. package/template/plugin/.gitignore +2 -0
  286. package/template/plugin/README.md +26 -0
  287. package/template/plugin/SKILLCDN.md +13 -0
  288. package/template/plugin/build.mjs +40 -0
  289. package/template/plugin/console-plugin.json +50 -0
  290. package/template/plugin/package.json +27 -0
  291. package/template/plugin/skills/__key__/SKILL.md +23 -0
  292. package/template/plugin/src/check.test.ts +34 -0
  293. package/template/plugin/src/server/index.test.ts +41 -0
  294. package/template/plugin/src/server/index.ts +72 -0
  295. package/template/plugin/src/web/index.css +51 -0
  296. package/template/plugin/src/web/index.ts +115 -0
  297. package/template/plugin/tsconfig.json +17 -0
  298. package/skills/working-the-board/references/setting-up.md +0 -3
  299. /package/skills/{working-the-board → planning-the-board}/references/project-rules.md +0 -0
@@ -0,0 +1,34 @@
1
+ // The package as a console reads it: built, then checked with `console-plugin check`, the same
2
+ // check a store would run. Fails on anything the console would refuse the plugin for.
3
+ import { execFile } from "node:child_process";
4
+ import { createRequire } from "node:module";
5
+ import { dirname, join } from "node:path";
6
+ import process from "node:process";
7
+ import { fileURLToPath, pathToFileURL } from "node:url";
8
+ import { promisify } from "node:util";
9
+ import { beforeAll, describe, expect, it } from "vitest";
10
+
11
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
12
+ const consoleDir = dirname(
13
+ createRequire(import.meta.url).resolve("@skillcdn/console/package.json"),
14
+ );
15
+ const consolePlugin = join(consoleDir, "dist", "cli", "plugin-main.js");
16
+
17
+ describe("the package", () => {
18
+ beforeAll(async () => {
19
+ const { buildPlugin } = (await import(pathToFileURL(join(root, "build.mjs")).href)) as {
20
+ buildPlugin: (root: string) => Promise<void>;
21
+ };
22
+ await buildPlugin(root);
23
+ }, 60_000);
24
+
25
+ it("passes console-plugin check", async () => {
26
+ const run = promisify(execFile);
27
+ const { stdout } = await run(process.execPath, [consolePlugin, "check", root]).catch(
28
+ (error: { stdout?: string; stderr?: string }) => {
29
+ throw new Error(`console-plugin check failed:\n${error.stdout ?? ""}${error.stderr ?? ""}`);
30
+ },
31
+ );
32
+ expect(stdout).toMatch(/^ok: /m);
33
+ }, 30_000);
34
+ });
@@ -0,0 +1,41 @@
1
+ import { createTestContext } from "@skillcdn/console/plugin/testing";
2
+ import { describe, expect, it } from "vitest";
3
+ import { greet, greetings, latest } from "./index.js";
4
+
5
+ describe("greet", () => {
6
+ it("keeps the greeting as a record and says so", async () => {
7
+ const ctx = createTestContext({
8
+ collections: [greetings],
9
+ plugin: { settings: { greeting: "Hi" } },
10
+ });
11
+ const result = await greet.run(ctx, { to: "world" });
12
+ expect(result.text).toContain("Hi, world!");
13
+ expect(ctx.all("greetings")).toHaveLength(1);
14
+ expect(ctx.all("greetings")[0]?.data).toEqual({
15
+ to: "world",
16
+ text: "Hi, world!",
17
+ by: "tester",
18
+ });
19
+ expect(ctx.emitted).toEqual([
20
+ { kind: "greeted", data: { id: expect.any(String), to: "world" } },
21
+ ]);
22
+ });
23
+
24
+ it("refuses what does not fit the schema", async () => {
25
+ const ctx = createTestContext({ collections: [greetings] });
26
+ await expect(greet.run(ctx, { to: "x".repeat(81) })).rejects.toMatchObject({
27
+ code: "plugin.invalid_args",
28
+ });
29
+ });
30
+ });
31
+
32
+ describe("latest", () => {
33
+ it("answers the newest first, as many as asked", async () => {
34
+ const ctx = createTestContext({ collections: [greetings] });
35
+ await greet.run(ctx, { to: "first" });
36
+ await greet.run(ctx, { to: "second" });
37
+ await greet.run(ctx, { to: "third" });
38
+ const items = (await latest.run(ctx, { limit: 2 })) as { data: { to: string } }[];
39
+ expect(items.map((item) => item.data.to)).toEqual(["third", "second"]);
40
+ });
41
+ });
@@ -0,0 +1,72 @@
1
+ // The server module: what the console runs in a thread of its own, reaching the console through
2
+ // `ctx` alone. Everything imported here is bundled into dist/server/index.js by the build.
3
+ import {
4
+ arg,
5
+ defineCollection,
6
+ defineCommand,
7
+ definePlugin,
8
+ defineQuery,
9
+ z,
10
+ } from "@skillcdn/console/plugin";
11
+
12
+ /** A greeting: who said what to whom. The schema is checked on every write. */
13
+ export const greetings = defineCollection("greetings", {
14
+ schema: z.object({
15
+ to: z.string().min(1).max(80),
16
+ text: z.string().min(1).max(200),
17
+ by: z.string().min(1),
18
+ }),
19
+ indexes: ["to", "by"],
20
+ search: ["text"],
21
+ owner: "by",
22
+ read: ["greeter"],
23
+ });
24
+
25
+ export const greet = defineCommand({
26
+ about: "Greets someone, and keeps the greeting as a record.",
27
+ args: {
28
+ to: arg.text({ about: "who is greeted", positional: true, max: 80 }),
29
+ text: arg.text({
30
+ about: "the words; the greeting setting's word and the name unless said",
31
+ optional: true,
32
+ max: 200,
33
+ }),
34
+ },
35
+ roles: ["greeter"],
36
+ async run(ctx, args) {
37
+ const word =
38
+ typeof ctx.plugin.settings.greeting === "string" ? ctx.plugin.settings.greeting : "Hello";
39
+ const text = args.text ?? `${word}, ${args.to}!`;
40
+ const by = ctx.caller.kind === "person" ? ctx.caller.person.login : "console";
41
+ const record = await ctx.records(greetings).create({ to: args.to, text, by });
42
+ await ctx.events.emit("greeted", { id: record.id, to: args.to });
43
+ return { text: `${text} (kept as ${record.id})`, data: record };
44
+ },
45
+ });
46
+
47
+ export const latest = defineQuery({
48
+ about: "The latest greetings, newest first.",
49
+ args: {
50
+ limit: arg.number({
51
+ about: "how many; ten unless said",
52
+ optional: true,
53
+ integer: true,
54
+ min: 1,
55
+ max: 50,
56
+ }),
57
+ },
58
+ roles: ["greeter"],
59
+ async run(ctx, args) {
60
+ const page = await ctx.records(greetings).list({
61
+ limit: args.limit ?? 10,
62
+ order: { field: "id", descending: true },
63
+ });
64
+ return page.items;
65
+ },
66
+ });
67
+
68
+ export default definePlugin({
69
+ collections: [greetings],
70
+ commands: { greet },
71
+ queries: { latest },
72
+ });
@@ -0,0 +1,51 @@
1
+ /* The plugin's own classes begin with pl-__key__-; the console's tokens and sc-* classes are there to use. */
2
+ .pl-__key__-page {
3
+ display: grid;
4
+ gap: var(--sc-space-4);
5
+ padding: var(--sc-space-4);
6
+ color: var(--sc-color-text);
7
+ font-family: var(--sc-font-sans);
8
+ }
9
+
10
+ .pl-__key__-form {
11
+ display: flex;
12
+ gap: var(--sc-space-2);
13
+ align-items: center;
14
+ }
15
+
16
+ .pl-__key__-list {
17
+ display: grid;
18
+ gap: var(--sc-space-2);
19
+ margin: 0;
20
+ padding: 0;
21
+ list-style: none;
22
+ }
23
+
24
+ .pl-__key__-greeting {
25
+ display: flex;
26
+ justify-content: space-between;
27
+ gap: var(--sc-space-3);
28
+ padding: var(--sc-space-2) var(--sc-space-3);
29
+ border: 1px solid var(--sc-color-border);
30
+ border-radius: var(--sc-radius-md);
31
+ background: var(--sc-color-bg-raised);
32
+ }
33
+
34
+ .pl-__key__-by {
35
+ color: var(--sc-color-text-muted);
36
+ font-size: var(--sc-text-sm);
37
+ }
38
+
39
+ /* The console's box around a widget pads it; the widget pads nothing of its own. */
40
+ .pl-__key__-widget {
41
+ color: var(--sc-color-text);
42
+ font-family: var(--sc-font-sans);
43
+ }
44
+
45
+ .pl-__key__-empty {
46
+ color: var(--sc-color-text-faint);
47
+ }
48
+
49
+ .pl-__key__-error {
50
+ color: var(--sc-color-danger);
51
+ }
@@ -0,0 +1,115 @@
1
+ // The web bundle: the page and the widget, in a frame the console serves. It speaks to the console
2
+ // through the bridge alone, and renders every word a person wrote as text, never as HTML.
3
+ import { connect, type PluginFrame, type PluginRecord } from "@skillcdn/console/plugin/web";
4
+
5
+ interface Greeting {
6
+ readonly to: string;
7
+ readonly text: string;
8
+ readonly by: string;
9
+ }
10
+
11
+ const PREFIX = "pl-__key__-";
12
+
13
+ function element<K extends keyof HTMLElementTagNameMap>(
14
+ tag: K,
15
+ className: string,
16
+ text?: string,
17
+ ): HTMLElementTagNameMap[K] {
18
+ const node = document.createElement(tag);
19
+ node.className = `${PREFIX}${className}`;
20
+ if (text !== undefined) {
21
+ node.textContent = text;
22
+ }
23
+ return node;
24
+ }
25
+
26
+ async function latestGreetings(
27
+ frame: PluginFrame,
28
+ limit: number,
29
+ ): Promise<readonly PluginRecord<Greeting>[]> {
30
+ return (await frame.query("latest", { limit })) as readonly PluginRecord<Greeting>[];
31
+ }
32
+
33
+ function greetingItem(record: PluginRecord<Greeting>): HTMLLIElement {
34
+ const item = element("li", "greeting");
35
+ item.append(
36
+ element("span", "text", record.data.text),
37
+ element("span", "by", `by ${record.data.by}`),
38
+ );
39
+ return item;
40
+ }
41
+
42
+ async function renderPage(root: HTMLElement, frame: PluginFrame): Promise<void> {
43
+ const page = element("div", "page");
44
+ const form = element("form", "form");
45
+ const input = document.createElement("input");
46
+ input.className = "sc-input";
47
+ input.placeholder = "Who to greet";
48
+ input.maxLength = 80;
49
+ input.required = true;
50
+ const button = document.createElement("button");
51
+ button.className = "sc-button";
52
+ button.type = "submit";
53
+ button.textContent = "Greet";
54
+ form.append(input, button);
55
+ const list = element("ul", "list");
56
+ const status = element("p", "empty");
57
+ page.append(form, list, status);
58
+ root.replaceChildren(page);
59
+
60
+ const refresh = async () => {
61
+ const records = await latestGreetings(frame, 20);
62
+ list.replaceChildren(...records.map(greetingItem));
63
+ status.textContent = records.length === 0 ? "Nobody has been greeted yet." : "";
64
+ status.className = `${PREFIX}empty`;
65
+ };
66
+ form.addEventListener("submit", (event) => {
67
+ event.preventDefault();
68
+ button.disabled = true;
69
+ frame
70
+ .command("greet", { to: input.value.trim() })
71
+ .then(() => {
72
+ input.value = "";
73
+ return refresh();
74
+ })
75
+ .catch((error: unknown) => {
76
+ status.textContent = error instanceof Error ? error.message : "The greeting was refused.";
77
+ status.className = `${PREFIX}error`;
78
+ })
79
+ .finally(() => {
80
+ button.disabled = false;
81
+ });
82
+ });
83
+ frame.onEvent(["records", "command"], () => {
84
+ void refresh();
85
+ });
86
+ frame.setTitle("Greetings");
87
+ await refresh();
88
+ }
89
+
90
+ async function renderWidget(root: HTMLElement, frame: PluginFrame): Promise<void> {
91
+ const widget = element("div", "widget");
92
+ root.replaceChildren(widget);
93
+ const refresh = async () => {
94
+ const [record] = await latestGreetings(frame, 1);
95
+ widget.replaceChildren(
96
+ record === undefined
97
+ ? element("p", "empty", "Nobody has been greeted yet.")
98
+ : element("p", "text", record.data.text),
99
+ );
100
+ };
101
+ frame.onEvent(["records"], () => {
102
+ void refresh();
103
+ });
104
+ await refresh();
105
+ }
106
+
107
+ const root = document.getElementById("root");
108
+ if (root !== null) {
109
+ const frame = await connect();
110
+ if (frame.context.widget !== undefined) {
111
+ await renderWidget(root, frame);
112
+ } else {
113
+ await renderPage(root, frame);
114
+ }
115
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2024",
4
+ "lib": ["ES2024", "DOM", "DOM.Iterable"],
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "strict": true,
8
+ "noUncheckedIndexedAccess": true,
9
+ "verbatimModuleSyntax": true,
10
+ "isolatedModules": true,
11
+ "erasableSyntaxOnly": true,
12
+ "types": ["node"],
13
+ "skipLibCheck": true,
14
+ "noEmit": true
15
+ },
16
+ "include": ["src"]
17
+ }
@@ -1,3 +0,0 @@
1
- # Setting up, when told to
2
-
3
- A task or a person may ask for the project itself rather than work in it: a project made or changed (`console project new <key> "<name>"`, `console project edit <key>`), its mark (`console project icon <key> <file>`), its members (`console member add <login>`, `console member role`, `console member remove`), the workspace's categories (`console category new <key> "<name>"`, `console category edit`, `console category remove`), what a person is (`console person role <login> <admin|member>`), its goals (`console goal new`), and its rules (`console rules write --file <path>`, from [the example](project-rules.md); the workspace's with `--workspace`, for an administrator). The agent does these as its person may, an owner of the project or an administrator of the workspace ([ADR-0021](/docs/adr/0021-a-token-configures-as-its-person-does.md)), and a project is made by an administrator alone ([ADR-0045](/docs/adr/0045-a-project-is-made-by-an-administrator.md)); the feed names the agent beside the person on each. What the person may not, the console refuses (`auth.forbidden`): that step goes back to the person in the report, never worked around. `console people` and `console members` say who is who, by login; `console workspace` says the workspace's settings, and `console workspace edit` changes them for an administrator: `--people-visibility <everyone|shared-projects>`, who sees whom on the People page; `--week-start <auto|monday|sunday>`, the day the calendars begin a week on; `--time-zone <zone|none>`, the workspace's time zone, an IANA name such as `Asia/Seoul`, in which the command prints moments ([ADR-0035](/docs/adr/0035-who-may-see-whom-is-decided-by-one-policy-and-the-workspace-keeps-its-settings.md)). In a project open to the workspace everyone is a member already: `console member add <login> --role owner` lists an owner, and `member role <login> member` takes one back; a plain `member add` is refused there (`member.open_project`). A login is found exactly, so an owner lists someone `console people` does not show them. A private pack is connected by an owner with `console project pack-token <key> --connect`, or, for the organization's pack, which projects read when they name no address of their own, by an administrator with `console workspace pack-token --connect` ([ADR-0036](/docs/adr/0036-a-private-pack-is-connected-with-a-code-a-person-approves-from-the-projects-settings.md)): the command prints a page at SkillCDN and a code, which the agent relays; the person approves there, signed in as someone who may open the repository, and the console keeps the SkillCDN token, shown to nobody. Until then `console skills` says the pack is not served to the console.