@skillcdn/console 0.4.1 → 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.
- package/README.md +19 -2
- package/SKILLCDN.md +7 -5
- package/dist/api.d.ts +4 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +4 -3
- package/dist/api.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +543 -34
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/format.d.ts +34 -3
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +277 -6
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +35 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/plugin-check.d.ts +21 -0
- package/dist/cli/plugin-check.d.ts.map +1 -0
- package/dist/cli/plugin-check.js +374 -0
- package/dist/cli/plugin-check.js.map +1 -0
- package/dist/cli/plugin-cli.d.ts +20 -0
- package/dist/cli/plugin-cli.d.ts.map +1 -0
- package/dist/cli/plugin-cli.js +123 -0
- package/dist/cli/plugin-cli.js.map +1 -0
- package/dist/cli/plugin-init.d.ts +20 -0
- package/dist/cli/plugin-init.d.ts.map +1 -0
- package/dist/cli/plugin-init.js +74 -0
- package/dist/cli/plugin-init.js.map +1 -0
- package/dist/cli/plugin-main.d.ts +3 -0
- package/dist/cli/plugin-main.d.ts.map +1 -0
- package/dist/cli/plugin-main.js +28 -0
- package/dist/cli/plugin-main.js.map +1 -0
- package/dist/client.d.ts +97 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +63 -2
- package/dist/client.js.map +1 -1
- package/dist/compat.d.ts +1 -1
- package/dist/compat.js +2 -2
- package/dist/components/board.d.ts +8 -0
- package/dist/components/board.d.ts.map +1 -1
- package/dist/components/board.js +59 -15
- package/dist/components/board.js.map +1 -1
- package/dist/components/event-feed.d.ts +1 -1
- package/dist/components/event-feed.d.ts.map +1 -1
- package/dist/components/event-feed.js +33 -1
- package/dist/components/event-feed.js.map +1 -1
- package/dist/components/plugin-frame.d.ts +52 -0
- package/dist/components/plugin-frame.d.ts.map +1 -0
- package/dist/components/plugin-frame.js +314 -0
- package/dist/components/plugin-frame.js.map +1 -0
- package/dist/components/plugin-settings.d.ts +38 -0
- package/dist/components/plugin-settings.d.ts.map +1 -0
- package/dist/components/plugin-settings.js +348 -0
- package/dist/components/plugin-settings.js.map +1 -0
- package/dist/components/shell.d.ts +2 -0
- package/dist/components/shell.d.ts.map +1 -1
- package/dist/components/shell.js +26 -1
- package/dist/components/shell.js.map +1 -1
- package/dist/components/workspace.d.ts +7 -1
- package/dist/components/workspace.d.ts.map +1 -1
- package/dist/components/workspace.js +1 -1
- package/dist/components/workspace.js.map +1 -1
- package/dist/console.css +327 -0
- package/dist/console.d.ts.map +1 -1
- package/dist/console.js +136 -26
- package/dist/console.js.map +1 -1
- package/dist/data.d.ts +7 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +19 -2
- package/dist/data.js.map +1 -1
- package/dist/i18n/en.d.ts +149 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +160 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/ko.d.ts.map +1 -1
- package/dist/i18n/ko.js +144 -0
- package/dist/i18n/ko.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin/args.d.ts +148 -0
- package/dist/plugin/args.d.ts.map +1 -0
- package/dist/plugin/args.js +369 -0
- package/dist/plugin/args.js.map +1 -0
- package/dist/plugin/bridge.d.ts +146 -0
- package/dist/plugin/bridge.d.ts.map +1 -0
- package/dist/plugin/bridge.js +125 -0
- package/dist/plugin/bridge.js.map +1 -0
- package/dist/plugin/capabilities.d.ts +20 -0
- package/dist/plugin/capabilities.d.ts.map +1 -0
- package/dist/plugin/capabilities.js +133 -0
- package/dist/plugin/capabilities.js.map +1 -0
- package/dist/plugin/context.d.ts +374 -0
- package/dist/plugin/context.d.ts.map +1 -0
- package/dist/plugin/context.js +2 -0
- package/dist/plugin/context.js.map +1 -0
- package/dist/plugin/define.d.ts +208 -0
- package/dist/plugin/define.d.ts.map +1 -0
- package/dist/plugin/define.js +482 -0
- package/dist/plugin/define.js.map +1 -0
- package/dist/plugin/definition-error.d.ts +11 -0
- package/dist/plugin/definition-error.d.ts.map +1 -0
- package/dist/plugin/definition-error.js +17 -0
- package/dist/plugin/definition-error.js.map +1 -0
- package/dist/plugin/limits.d.ts +67 -0
- package/dist/plugin/limits.d.ts.map +1 -0
- package/dist/plugin/limits.js +70 -0
- package/dist/plugin/limits.js.map +1 -0
- package/dist/plugin/load-worker.d.ts +7 -0
- package/dist/plugin/load-worker.d.ts.map +1 -0
- package/dist/plugin/load-worker.js +41 -0
- package/dist/plugin/load-worker.js.map +1 -0
- package/dist/plugin/load.d.ts +18 -0
- package/dist/plugin/load.d.ts.map +1 -0
- package/dist/plugin/load.js +62 -0
- package/dist/plugin/load.js.map +1 -0
- package/dist/plugin/manifest.d.ts +237 -0
- package/dist/plugin/manifest.d.ts.map +1 -0
- package/dist/plugin/manifest.js +411 -0
- package/dist/plugin/manifest.js.map +1 -0
- package/dist/plugin/refusals.d.ts +49 -0
- package/dist/plugin/refusals.d.ts.map +1 -0
- package/dist/plugin/refusals.js +79 -0
- package/dist/plugin/refusals.js.map +1 -0
- package/dist/plugin/rest-schemas.d.ts +556 -0
- package/dist/plugin/rest-schemas.d.ts.map +1 -0
- package/dist/plugin/rest-schemas.js +262 -0
- package/dist/plugin/rest-schemas.js.map +1 -0
- package/dist/plugin/sandbox.d.ts +23 -0
- package/dist/plugin/sandbox.d.ts.map +1 -0
- package/dist/plugin/sandbox.js +99 -0
- package/dist/plugin/sandbox.js.map +1 -0
- package/dist/plugin/testing.d.ts +45 -0
- package/dist/plugin/testing.d.ts.map +1 -0
- package/dist/plugin/testing.js +420 -0
- package/dist/plugin/testing.js.map +1 -0
- package/dist/plugin/web-react.d.ts +11 -0
- package/dist/plugin/web-react.d.ts.map +1 -0
- package/dist/plugin/web-react.js +34 -0
- package/dist/plugin/web-react.js.map +1 -0
- package/dist/plugin/web.d.ts +95 -0
- package/dist/plugin/web.d.ts.map +1 -0
- package/dist/plugin/web.js +245 -0
- package/dist/plugin/web.js.map +1 -0
- package/dist/plugin-nav.d.ts +29 -0
- package/dist/plugin-nav.d.ts.map +1 -0
- package/dist/plugin-nav.js +92 -0
- package/dist/plugin-nav.js.map +1 -0
- package/dist/plugin-testing.d.ts +2 -0
- package/dist/plugin-testing.d.ts.map +1 -0
- package/dist/plugin-testing.js +4 -0
- package/dist/plugin-testing.js.map +1 -0
- package/dist/plugin-web-react.d.ts +3 -0
- package/dist/plugin-web-react.d.ts.map +1 -0
- package/dist/plugin-web-react.js +3 -0
- package/dist/plugin-web-react.js.map +1 -0
- package/dist/plugin-web.d.ts +4 -0
- package/dist/plugin-web.d.ts.map +1 -0
- package/dist/plugin-web.js +5 -0
- package/dist/plugin-web.js.map +1 -0
- package/dist/plugin.d.ts +11 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +15 -0
- package/dist/plugin.js.map +1 -0
- package/dist/router.d.ts +28 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +47 -0
- package/dist/router.js.map +1 -1
- package/dist/routes.d.ts +4 -0
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +6 -0
- package/dist/routes.js.map +1 -1
- package/dist/schemas.d.ts +117 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +40 -1
- package/dist/schemas.js.map +1 -1
- package/dist/tokens.d.ts +2 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +101 -0
- package/dist/tokens.js.map +1 -0
- package/dist/vocabulary.d.ts +11 -1
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +24 -0
- package/dist/vocabulary.js.map +1 -1
- package/dist/web.d.ts.map +1 -1
- package/dist/web.js +2 -0
- package/dist/web.js.map +1 -1
- package/docs/specs/cli.md +11 -4
- package/docs/specs/plugins.md +487 -0
- package/docs/specs/rest.md +3 -2
- package/package.json +20 -2
- package/skills/building-a-console/SKILL.md +25 -26
- package/skills/building-a-console/references/configuring.md +40 -0
- package/skills/building-a-console/references/the-default-console.md +1 -1
- package/skills/building-a-console/references/the-look-from-a-website.md +5 -5
- package/skills/building-a-plugin/SKILL.md +137 -0
- package/skills/deploying-a-console/SKILL.md +24 -26
- package/skills/deploying-a-console/references/access.md +13 -9
- package/skills/deploying-a-console/references/resources.md +25 -10
- package/skills/deploying-a-console/references/running-it-again.md +15 -0
- package/skills/deploying-a-console/references/sizing.md +2 -0
- package/skills/planning-the-board/SKILL.md +96 -0
- package/skills/planning-the-board/references/a-task.md +36 -0
- package/skills/planning-the-board/references/setting-up.md +44 -0
- package/skills/working-the-board/SKILL.md +43 -34
- package/skills/working-the-board/references/the-pack.md +19 -0
- package/src/api.ts +8 -0
- package/src/cli/cli.ts +697 -33
- package/src/cli/format.ts +356 -9
- package/src/cli/help.ts +41 -2
- package/src/cli/plugin-check.ts +470 -0
- package/src/cli/plugin-cli.ts +148 -0
- package/src/cli/plugin-init.ts +90 -0
- package/src/cli/plugin-main.ts +30 -0
- package/src/client.ts +294 -0
- package/src/compat.ts +2 -2
- package/src/components/board.tsx +62 -14
- package/src/components/event-feed.tsx +42 -1
- package/src/components/plugin-frame.tsx +437 -0
- package/src/components/plugin-settings.tsx +1111 -0
- package/src/components/shell.tsx +82 -14
- package/src/components/workspace.tsx +18 -0
- package/src/console.tsx +286 -19
- package/src/data.ts +27 -2
- package/src/i18n/en.ts +168 -0
- package/src/i18n/ko.ts +148 -0
- package/src/index.ts +12 -0
- package/src/plugin/args.ts +526 -0
- package/src/plugin/bridge.ts +314 -0
- package/src/plugin/capabilities.ts +150 -0
- package/src/plugin/context.ts +416 -0
- package/src/plugin/define.ts +792 -0
- package/src/plugin/definition-error.ts +19 -0
- package/src/plugin/limits.ts +70 -0
- package/src/plugin/load-worker.ts +50 -0
- package/src/plugin/load.ts +82 -0
- package/src/plugin/manifest.ts +561 -0
- package/src/plugin/refusals.ts +94 -0
- package/src/plugin/rest-schemas.ts +354 -0
- package/src/plugin/sandbox.ts +123 -0
- package/src/plugin/testing.ts +612 -0
- package/src/plugin/web-react.ts +45 -0
- package/src/plugin/web.ts +403 -0
- package/src/plugin-nav.ts +132 -0
- package/src/plugin-testing.ts +10 -0
- package/src/plugin-web-react.ts +4 -0
- package/src/plugin-web.ts +35 -0
- package/src/plugin.ts +126 -0
- package/src/router.ts +73 -0
- package/src/routes.ts +7 -0
- package/src/schemas.ts +43 -0
- package/src/styles/console.css +327 -0
- package/src/tokens.ts +101 -0
- package/src/vocabulary.ts +30 -0
- package/src/web.ts +2 -0
- package/template/plugin/.gitignore +2 -0
- package/template/plugin/README.md +26 -0
- package/template/plugin/SKILLCDN.md +13 -0
- package/template/plugin/build.mjs +40 -0
- package/template/plugin/console-plugin.json +50 -0
- package/template/plugin/package.json +27 -0
- package/template/plugin/skills/__key__/SKILL.md +23 -0
- package/template/plugin/src/check.test.ts +34 -0
- package/template/plugin/src/server/index.test.ts +41 -0
- package/template/plugin/src/server/index.ts +72 -0
- package/template/plugin/src/web/index.css +51 -0
- package/template/plugin/src/web/index.ts +115 -0
- package/template/plugin/tsconfig.json +17 -0
- package/skills/working-the-board/references/setting-up.md +0 -3
- /package/skills/{working-the-board → planning-the-board}/references/project-rules.md +0 -0
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: deploying-a-console
|
|
3
|
-
description: "Deploys a SkillCDN Console for an organization on AWS behind Cloudflare,
|
|
3
|
+
description: "Deploys a SkillCDN Console for an organization on AWS behind Cloudflare, from a person's machine, no developer needed: a few plain questions (how many people, the address, how people sign in and who is in, the organization's skills, its website, whose look it takes by default), the monthly cost before anything is made, then the database, bucket, secrets, tunnel and containers, a private repository that redeploys on every push, the first administrator signed in, the first project, an agent connected and proven with a file handed in. Also grows it, updates it to a new version, installs a plugin, adds a member or rotates a secret. Use when a person says 'set up a console for our company', 'deploy the console', 'host the console on AWS', 'upgrade our console', 'we need a bigger size'. To build the pages alone, use building-a-console; to write a plugin, building-a-plugin; to work tasks, working-the-board. Every purchase and push is confirmed with its price; nothing is spent without the person's word."
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Needs a shell with Node.js 24 or newer, git, curl, jq and the AWS CLI v2, the gh command signed in or an empty repository the person made, network access to AWS, Cloudflare, GitHub and npm, an AWS account and a domain on Cloudflare the person can create a key and a token for, and a browser on any device for the few steps only a signed-in person can take. Works in any agent that can run a shell and write files; on Windows, in Git Bash with path conversion off.
|
|
6
6
|
metadata:
|
|
7
7
|
author: skillcdn
|
|
8
|
-
version: "1.
|
|
8
|
+
version: "1.7"
|
|
9
9
|
skillcdn:
|
|
10
10
|
translations:
|
|
11
11
|
ko:
|
|
12
12
|
title: 조직의 콘솔 배포하기
|
|
13
|
-
description: "조직의 SkillCDN 콘솔을 AWS와 Cloudflare에 배포합니다. 개발자가 아니어도 됩니다:
|
|
13
|
+
description: "조직의 SkillCDN 콘솔을 AWS와 Cloudflare에 배포합니다. 개발자가 아니어도 됩니다: 쉬운 질문 몇 개(몇 명이 쓰는지, 주소, 로그인 방식과 구성원, 조직의 스킬, 디자인의 기본이 되는 웹사이트)를 묻고, 무엇을 만들기 전에 맞는 크기와 그 다음 크기의 월 비용을 보여준 뒤, 데이터베이스·버킷·비밀·터널·컨테이너를 만들고, 조직의 디자인을 이미지에 담고, 푸시마다 다시 배포되는 비공개 저장소를 남기고, 첫 관리자를 로그인시키고, 첫 프로젝트를 만들고, 에이전트를 연결해 파일 제출과 되읽기로 증명합니다. 규모 키우기, 새 버전으로 올리기, 이미지에 플러그인 설치, 구성원 추가, 비밀 교체도 합니다. '우리 회사 콘솔 세팅해줘', '콘솔 배포해줘', 'AWS에 콘솔 올려줘', '콘솔 업그레이드해줘', '더 큰 크기가 필요해', '연차 플러그인 설치해줘'라고 할 때 쓰세요. 페이지만 만들 때는 building-a-console, 플러그인을 만들 때는 building-a-plugin, 작업을 할 때는 working-the-board를 쓰세요. 구매와 푸시는 하나하나 가격과 함께 확인받고, 사람의 말 없이는 아무것도 쓰지 않습니다."
|
|
14
14
|
---
|
|
15
15
|
# Deploying a console for an organization
|
|
16
16
|
|
|
@@ -20,10 +20,10 @@ What goes in: an AWS account, a domain on Cloudflare or one to buy, and the answ
|
|
|
20
20
|
|
|
21
21
|
- **Questions:** one message at the start, the ones under "Inputs" that the request leaves open, in plain words. Then a second message, so that the questions stay short: the order of cost for the size ([sizing.md](references/sizing.md)'s snapshot, named as such) and the two credentials to create ([access.md](references/access.md)), so that the person knows what it costs before an account or a key exists, and the credentials are ready when the plan is.
|
|
22
22
|
- **Checkpoints:** the plan with today's cost, before anything is made; each purchase with its price (a domain); a secret only the person holds, asked with the exact steps once the address is known; the first push, which starts the first build and makes the address public once it serves; the record's push; the deletion of the secrets file and the token file. One short message each, with the recommendation, and "OK", in the person's language, continues.
|
|
23
|
-
- **Go-ahead:** when the person says to go ahead alone, the reports between phases need no answer. The plan, a purchase,
|
|
24
|
-
- **Waiting:** what only the person or the world can finish, the run waits for, in the open. A domain whose nameservers the person must change answers at Cloudflare minutes to hours later: the zone is made
|
|
23
|
+
- **Go-ahead:** when the person says to go ahead alone, the reports between phases need no answer. The plan, a purchase, every push and a deletion are confirmed in every mode.
|
|
24
|
+
- **Waiting:** what only the person or the world can finish, the run waits for, in the open. A domain whose nameservers the person must change answers at Cloudflare minutes to hours later: the zone, which costs nothing and is undone as easily, is made in phase 1, the one thing made before the plan, so that the steps go out in the second message, and from phase 5 the run checks the zone every ten minutes for up to an hour, saying so; still not there, it stops with the record saying what waits and resumes at phase 5 when the person says the nameservers are set. The only way of signing in answered `later` means nobody can sign in: the run stops at phase 5 the same way and resumes when the secret is in the secrets file.
|
|
25
25
|
- **Changes mid-run:** applied from that point. What was created is kept and recorded, never silently deleted.
|
|
26
|
-
- **Language:** messages to the person in the language they wrote in; the repository's files in English, its `README.md` and `DEPLOY.md` in the
|
|
26
|
+
- **Language:** messages to the person in the language they wrote in; the repository's files in English, its `README.md` and `DEPLOY.md` in the language the person wrote in, unless they name another.
|
|
27
27
|
|
|
28
28
|
## Requirements
|
|
29
29
|
|
|
@@ -32,7 +32,7 @@ What goes in: an AWS account, a domain on Cloudflare or one to buy, and the answ
|
|
|
32
32
|
| A shell with Node.js 24 or newer, git, curl, jq and the AWS CLI v2 (`aws --version`); on Windows, Git Bash with `export MSYS_NO_PATHCONV=1`, since Git Bash otherwise rewrites an argument that starts with `/` (a log group, a parameter) into a Windows path before a program sees it | Every command of the run; the deployment repository |
|
|
33
33
|
| The deployer's access key and the Cloudflare token, as [access.md](references/access.md) says they are made and reach the agent | Everything on AWS and at Cloudflare |
|
|
34
34
|
| The `gh` command signed in (`gh auth status`), or an empty private repository the person made | The deployment repository, its first push, watching its runs |
|
|
35
|
-
| The `console` command, `npm install -g @skillcdn/console` in phase 1 | The proof at the end: an agent connected, a file handed in; and this skill's two scripts, which the package carries at `$(npm root -g)/@skillcdn/console/skills/deploying-a-console/scripts/`, copied into the run's `scratch/` folder, or read through the connection and written there |
|
|
35
|
+
| The `console` command, `npm install -g @skillcdn/console` in phase 1 | The proof at the end: an agent connected, a file handed in; and this skill's two scripts, which the package carries at `$(npm root -g)/@skillcdn/console/skills/deploying-a-console/scripts/`, copied into the run's `scratch/` folder, or read through the connection and written there; and the skill building-a-console, beside this one at `$(npm root -g)/@skillcdn/console/skills/building-a-console/SKILL.md` or through the connection, for the look and `console-ui/` |
|
|
36
36
|
| The person, with a browser on any device | The GitHub App or the Google client, the first sign-in, approving the agent's code, a registrar's nameservers |
|
|
37
37
|
| Network access to AWS, Cloudflare, GitHub and npm | |
|
|
38
38
|
|
|
@@ -42,19 +42,20 @@ Check these before the first message. Without the AWS CLI, offer to install it (
|
|
|
42
42
|
|
|
43
43
|
| Input | Source |
|
|
44
44
|
|---|---|
|
|
45
|
-
| How many people | Asked: "How many people will use the console within a year: just you, up to 10, up to 30, or up to 100?" The answer picks the size ([sizing.md](references/sizing.md)). |
|
|
46
|
-
| The board's name | Suggested from the convention and confirmed, never invented: the organization or the person the console is for, then Console, as `SkillCDN Console` is named. With the organization's name in the request or on its website: "The board will be called `Acme Console`, as consoles are named: the organization's name, then Console. Keep it, or say another?" Without
|
|
47
|
-
| The web address | Asked: "Which web address should the console have? A domain you already own, and where it is registered (I set it up on Cloudflare; you change its nameservers
|
|
45
|
+
| How many people | Asked: "How many people will use the console within a year: just you, up to 10, up to 30, or up to 100?" The answer picks the size ([sizing.md](references/sizing.md)); more than 100 is `large` with its numbers raised at the plan, and said so. |
|
|
46
|
+
| The board's name | Suggested from the convention and confirmed, never invented: the organization or the person the console is for, then Console, as `SkillCDN Console` is named. With the organization's name in the request or on its website: "The board will be called `Acme Console`, as consoles are named: the organization's name, then Console. Keep it, or say another?" With an organization and no name: "What is your organization's name? Its name, then Console, is what the board is called." Without either: "Whose console is this, your organization's or yours? Its name, then Console, is what the board is called." It goes to `WORKSPACE_NAME`, one line of up to 100 characters: what the pages say after the mark, and what the console calls itself at SkillCDN when it connects a pack. |
|
|
47
|
+
| The web address | Asked: "Which web address should the console have? A domain you already own, and where it is registered (I set it up on Cloudflare; you change its nameservers at your registrar once, and I give you the steps; your website and your mail keep working, since their records come over as they are), or should I find one to buy (I show the price before buying)? I suggest `console.<domain>`." A domain is needed: the tunnel has no free hostname. A domain that already serves the organization's site is fine: only the console's hostname is added to its zone, and nothing of the site is touched (resources.md "A zone that serves other hosts"). |
|
|
48
48
|
| How people sign in, and who | Asked: "How should people sign in: with GitHub, with Google (Workspace), or both? Who configures the console (you, at least): their GitHub login or Google address. With GitHub, everyone of your organization can be in, as the organization has them: say which organization. With a Google Workspace, everyone of the domain is in. Anyone else, an administrator lets in on the People page once the console runs." |
|
|
49
49
|
| The organization's skills | Asked: "Where are your organization's skills on SkillCDN, if anywhere yet? An address like `/gh/<owner>/<repo>`. Is that repository private?" Default: none; a project names one later on its Settings page. A private one is connected in phase 6, with a code the person approves at SkillCDN ([ADR-0036](/docs/adr/0036-a-private-pack-is-connected-with-a-code-a-person-approves-from-the-projects-settings.md)); nothing of it is needed before. |
|
|
50
50
|
| The organization's website, and the look | Asked: "Does your organization have a website? Its address." With one, the console takes the organization's look by default: the marks, the colours and the type read from the site as building-a-console's [the-look-from-a-website.md](/skills/building-a-console/references/the-look-from-a-website.md) says, mapped onto the pages and shown at the plan, and built into the image as the organization's own console, `console-ui/` of the deployment repository, so that the first deploy already carries it. Without a site, or when the person says the default pages are fine, the image's default pages serve: the board's name alone, and no marks. A look that is wanted later is the same skill, building-a-console, in `console-ui/`, and a push. |
|
|
51
|
+
| The plugins | Asked only when the request names one, or the organization's console is to carry a feature of its work (leave, customers, money): "Should the console carry a plugin? Which package, at which version or tag?" Default: none. A plugin is installed by building it into the image from `plugins/package.json` and its lockfile in the deployment repository, with no install scripts ([deploy/README.md "Plugins in the image"](/deploy/README.md#plugins-in-the-image)), and enabled by the administrator on the console's settings page once it runs, consenting to what it asks; one with `storage:sql` needs the database user to hold `CREATEROLE`, which the master user of phase 3's database does. One wanted later is a row of [running-it-again.md](references/running-it-again.md); one to be written is [building-a-plugin](/skills/building-a-plugin/SKILL.md). |
|
|
51
52
|
| The AWS account | Asked: "Do you have an AWS account for this, or should the console get an account of its own (the simplest to run and to read the bill of)?" A new account is made by the person, on the paid plan, with the order of cost already in front of them (the second message). |
|
|
52
|
-
| The region | Asked when the organization's country does not say: "
|
|
53
|
+
| The region | Asked when the organization's country does not say: "Where are most of your people: a country, or a city?" The region is the one nearest them: `ap-northeast-2` (Seoul), `ap-northeast-1` (Tokyo), `ap-southeast-1` (Singapore), `eu-central-1` (Frankfurt), `eu-west-1` (Ireland), `eu-west-2` (London), `us-east-1` (N. Virginia), `us-east-2` (Ohio), `us-west-2` (Oregon); `aws ec2 describe-regions` lists them all, and sizing.md says how a region's prices are read. |
|
|
53
54
|
| The run's folder | Asked with a suggestion: "Which folder should the deployment live in on this machine? I suggest `console-deploy` under your home folder." It is made at once and becomes the deployment repository. |
|
|
54
55
|
| The deployment repository | Asked: "Where should the deployment's files live on GitHub: a private repository under your account, or under an organization you name?" Its name is the folder's. |
|
|
55
56
|
| Where alerts go | Asked: "Which email should the cost alert go to?" |
|
|
56
57
|
| The console's version | Derived: the latest release of the console, the tag `@skillcdn/console@<version>` of its repository: the last line of `git ls-remote --tags https://github.com/skillcdn/console 'refs/tags/@skillcdn/console@*' \| sort -t@ -k3 -V` is the highest version, and its first column the commit (an annotated tag lists its commit on a `^{}` line, which sorts last the same way), pinned in the deployment repository as `CONSOLE_REF`. An organization's own console (building-a-console) depends on the package at that same version, so that the pages and the server are one release. An update is the next tag, a new commit there. |
|
|
57
|
-
| Everything else | Derived and shown in the plan: the size's numbers
|
|
58
|
+
| Everything else | Derived and shown in the plan: the size's numbers and the names of everything made (resources.md "Phase 2"); the generated secrets (`AUTH_SECRET`, the database's password); the variables of [deploy/README.md](/deploy/README.md) with the tunnel's settings (`TRUSTED_PROXIES=127.0.0.1,::1`, `CLIENT_IP_HEADER=cf-connecting-ip`, `HTTP_KEEP_ALIVE_SECONDS=100`) and the bucket read and written as the task's own role (`S3_CREDENTIALS=platform`), so that no key exists for it; the connector's image, mirrored into the organization's image repository; the estimate of this size and the next, the budget alert at one and a half times the estimate, the rate limiting rule on the sign-in paths, and the few steps the person takes once. |
|
|
58
59
|
|
|
59
60
|
## Workflow
|
|
60
61
|
|
|
@@ -70,11 +71,11 @@ Produces the **plan**, shown at the checkpoint. From the answers: the size and w
|
|
|
70
71
|
|
|
71
72
|
### Phase 3: Foundation
|
|
72
73
|
|
|
73
|
-
Produces the **resource record**: each resource with its name and id. In this order: the zone at Cloudflare, with the records the domain serves already found by Cloudflare's scan and checked by the person against their registrar's DNS page, so that nameservers propagate meanwhile; the network and its two security groups; the database, started, and waited for while the rest is made; the bucket; the image repository, the log group, the roles, the task's with the bucket's policy, the cluster; the tunnel, the hostname, always-HTTPS for the hostname and the rate limiting rule; the parameters, generated and derived, from the secrets file.
|
|
74
|
+
Produces the **resource record**: each resource with its name and id. In this order: the zone at Cloudflare, with the records the domain serves already found by Cloudflare's scan and checked by the person against their registrar's DNS page, so that nameservers propagate meanwhile; the network and its two security groups; the database, started, and waited for while the rest is made; the bucket; the image repository, the log group, the roles, the task's with the bucket's policy, the cluster; the tunnel, the hostname, always-HTTPS for the hostname and the rate limiting rule; the parameters, generated and derived, from the secrets file. The message for each secret the person holds (access.md "The GitHub App", "The OAuth client") goes out right after the plan's word, the hostname being fixed there, so that they do it while the foundation is made: "Save it in the secrets file as the line I show, or say `later` and that way of signing in stays off until it is set." A value the person pastes into the chat instead is written into the file by the agent's file tool, never repeated, and the person is told to generate a new one once the run is over, since the chat kept a copy; the new one goes in as "a secret rotated" of the record.
|
|
74
75
|
|
|
75
76
|
### Phase 4: The deployment repository
|
|
76
77
|
|
|
77
|
-
Produces the **repository**: `deploy/console.env`, the task definitions `deploy/migrate.task.json`, `deploy/api.task.json` and, at the large size, `deploy/worker.task.json`, the workflow `.github/workflows/deploy.yml`, `README.md` and a draft `DEPLOY.md`, as resources.md gives them, every value filled from the record; with the organization's look, `console-ui/`, the organization's own console made here with building-a-console at the console's pinned version, from the look approved at the plan, which the workflow builds into the image on every push (resources.md "The organization's own console"); and on AWS the role the workflow assumes, trusted for this repository's `main` alone. Show the files, each with the lines that matter pointed out for a person who is not a developer, and the look's pages where a browser can be driven; commit; create the empty private repository. Checkpoint: "Pushing starts the first build (about ten minutes), runs the migrations, and starts the console at <hostname>. OK?" On the word, push, and watch the run to its end.
|
|
78
|
+
Produces the **repository**: `deploy/console.env`, the task definitions `deploy/migrate.task.json`, `deploy/api.task.json` and, at the large size, `deploy/worker.task.json`, the workflow `.github/workflows/deploy.yml`, `README.md` and a draft `DEPLOY.md`, as resources.md gives them, every value filled from the record; with the organization's look, `console-ui/`, the organization's own console made here with building-a-console at the console's pinned version, from the look approved at the plan, which the workflow builds into the image on every push (resources.md "The organization's own console"); with a plugin named at the plan, `plugins/package.json` and its lockfile, which the workflow installs into the image (resources.md "The plugins"); and on AWS the role the workflow assumes, trusted for this repository's `main` alone. Show the files, each with the lines that matter pointed out for a person who is not a developer, and the look's pages where a browser can be driven; commit; create the empty private repository. Checkpoint: "Pushing starts the first build (about ten minutes), runs the migrations, and starts the console at <hostname>. OK?" On the word, push, and watch the run to its end.
|
|
78
79
|
|
|
79
80
|
### Phase 5: Live
|
|
80
81
|
|
|
@@ -90,11 +91,11 @@ Produces the **proof**: `console task new "Deployment check"` with a body that s
|
|
|
90
91
|
|
|
91
92
|
### Phase 8: Record and delivery
|
|
92
93
|
|
|
93
|
-
Produces **`DEPLOY.md`** in the repository, from the template in resources.md: what exists under which names, the size and its estimate, how a deploy happens, how to add a member, update, grow, rotate a secret, pause and remove, and what is owed. Checkpoint: "Committing and pushing the record. OK?" (a change to the record alone does not redeploy). Then, on the person's word, delete the secrets file and the token file. Deliver in one message: the address, what was made in the person's words, the estimate and the alert, what the person still owes (a `later` secret, nameservers), what was verified and how, and how the console is operated from here: the record, and a push.
|
|
94
|
+
Produces **`DEPLOY.md`** in the repository, from the template in resources.md: what exists under which names, the size and its estimate, how a deploy happens, how to add a member, update, grow, rotate a secret, pause and remove, and what is owed. Checkpoint: "Committing and pushing the record. OK?" (a change to the record alone does not redeploy). Then, on the person's word, delete the secrets file and the token file; the profile `console` stays in the AWS CLI's credentials, since the record's commands run as it, until the deployment is removed. Deliver in one message: the address, what was made in the person's words, the estimate and the alert, what the person still owes (a `later` secret, nameservers), what was verified and how, and how the console is operated from here: the record, and a push.
|
|
94
95
|
|
|
95
96
|
### Running it again
|
|
96
97
|
|
|
97
|
-
The same skill, from phase 2, with the plan naming only what changes: a new version
|
|
98
|
+
The same skill, from phase 2, with the plan naming only what changes and its price: a new version of the console or of the connector, a plugin installed, updated or removed, the next size, a person let in or taken out, a secret rotated, a console shared with outside teams or customers, the console paused or removed. What each takes is in [running-it-again.md](references/running-it-again.md); each is confirmed at the plan, and the record is updated with it.
|
|
98
99
|
|
|
99
100
|
### Verdicts
|
|
100
101
|
|
|
@@ -112,7 +113,7 @@ The same skill, from phase 2, with the plan naming only what changes: a new vers
|
|
|
112
113
|
3. The first push is confirmed, and every push after it: once the repository is connected, a push is a deployment, which the person chose at the plan.
|
|
113
114
|
4. The person is never asked to do what the key and the token can do; what only they can do (a GitHub App or a Google client, a sign-in, approving a code, nameservers at their registrar) is given as the exact steps of the day in one message, for the registrar they named where its steps are known and in general words otherwise, so that they can do it while the run goes on.
|
|
114
115
|
5. Nothing of the deployment listens on the internet: the tasks' security group has no inbound rule, the database is not publicly accessible, the bucket blocks public access, and the hostname reaches the console only through the tunnel.
|
|
115
|
-
6. Names and ids go into the repository and the record; a value a secret scanner would flag never does, and `scratch/`, the key file and the token file never enter git. Every file
|
|
116
|
+
6. Names and ids go into the repository and the record; a value a secret scanner would flag never does, and `scratch/`, the key file and the token file never enter git. Every file written by hand is shown before it is pushed; a generated file (a lockfile, a build) is named.
|
|
116
117
|
7. What was created is kept and recorded, never silently deleted; removing the deployment is a run of its own, asked for, and confirmed resource by resource.
|
|
117
118
|
8. The console's contract holds on every rollout: `migrate` runs and exits `0` before the new `api` and `worker` start, as the one-off task of the workflow, or, without a pipeline, as a container of every task of both roles that its own container waits on (resources.md "Without a pipeline"); the stop timeout stays above the shutdown grace period, and no secret is a build argument.
|
|
118
119
|
|
|
@@ -120,13 +121,10 @@ The same skill, from phase 2, with the plan naming only what changes: a new vers
|
|
|
120
121
|
|
|
121
122
|
| Term | Meaning |
|
|
122
123
|
|---|---|
|
|
123
|
-
|
|
|
124
|
-
| The
|
|
125
|
-
| The
|
|
126
|
-
| The run's folder | The folder the person chose, which becomes the deployment repository; `scratch/`, the key file and the token file in it never enter git; the secrets file is beside it, in the folder above. |
|
|
124
|
+
| The deployer | The IAM user whose key the agent acts with, the profile `console` of the AWS CLI. |
|
|
125
|
+
| The task's role | `console-task`, the role the containers run as, with credentials the platform hands them and renews: it may read and write the bucket, and nothing else; no key exists for the files. |
|
|
126
|
+
| The run's folder | The folder the person chose, which becomes the deployment repository; `scratch/`, the key file and the token file in it never enter git. |
|
|
127
127
|
| The tunnel | The Cloudflare Tunnel whose connector runs beside the `api` container; the hostname's traffic comes in through it and nothing else does. |
|
|
128
|
-
| Task, service | On ECS, a task is the containers of one role running together
|
|
129
|
-
|
|
|
130
|
-
| The
|
|
131
|
-
| `DEPLOY.md` | The record: what exists under which names, how it is run, what is owed, what it costs. |
|
|
132
|
-
| Later | A secret the person did not supply; the way of signing in it serves stays off, and the record says so. |
|
|
128
|
+
| Task, service, parameter | On ECS, a task is the containers of one role running together, and a service keeps the wanted number of tasks running and rolls them to a new definition; a parameter is a value in SSM Parameter Store that a task reads as an environment variable at start. |
|
|
129
|
+
| The deployment repository, the record | The private repository of the task definitions, the workflow and `DEPLOY.md`, the record of what exists under which names, how it is run, what is owed and what it costs; a push to it is a deployment. |
|
|
130
|
+
| The secrets file; the key file and the token file | `console-secrets.json` in the folder above the run's folder, the values the person holds and the run generates, each stored as a parameter (access.md); `.aws-key.csv` and `.cf-token` in the run's folder, saved there by the person and read by the agent into the profile and the header file. None enters git, and each is deleted when the run is over, the secrets file on the person's word. |
|
|
@@ -44,26 +44,28 @@ The first answers with the account id the plan names; the second with the defaul
|
|
|
44
44
|
| Zone | Zone WAF | Edit | The rate limiting rule |
|
|
45
45
|
| Account | Registrar | Edit | Only when a domain is to be bought in the run |
|
|
46
46
|
|
|
47
|
-
Zone Resources: Include, All zones, so that a zone made in the run is covered. Account Resources: the one account. Client IP filtering: none. TTL: none, or a year. Continue to summary, Create Token, copy the secret once. Read where Edit is needed is the mistake to look for: a token with Read lists tunnels and rules and fails only at the first creation, with `403` and code `10000` (resources.md "When a step fails").
|
|
47
|
+
Ask for every row at once, Registrar excepted, which is asked only when a domain is to be bought: whether the zone serves other hosts, which decides between Zone Settings and Dynamic URL Redirects, is learned only once the token exists. Zone Resources: Include, All zones, so that a zone made in the run is covered. Account Resources: the one account. Client IP filtering: none. TTL: none, or a year. Continue to summary, Create Token, copy the secret once. Read where Edit is needed is the mistake to look for: a token with Read lists tunnels and rules and fails only at the first creation, with `403` and code `10000` (resources.md "When a step fails").
|
|
48
48
|
|
|
49
49
|
**How it reaches the agent.** Save it in a file named `.cf-token` in the run's folder (a file without an extension: in Notepad, "All files" as the type, so that it is not `.cf-token.txt`), and tell the agent; the agent reads it into the environment for each call and never prints it. The file is listed in the deployment repository's `.gitignore` from the first commit, and deleted at the end of the run on the person's word. A token pasted into the chat is written into the file with the agent's file tool and never repeated, and the person is told to make a new token once the run is over, since the chat kept a copy.
|
|
50
50
|
|
|
51
51
|
**Verify:**
|
|
52
52
|
|
|
53
53
|
```sh
|
|
54
|
-
|
|
55
|
-
curl -s https://api.cloudflare.com/client/v4/user/tokens/verify -H
|
|
56
|
-
curl -s https://api.cloudflare.com/client/v4/zones -H
|
|
57
|
-
curl -s https://api.cloudflare.com/client/v4/accounts -H
|
|
54
|
+
printf 'Authorization: Bearer %s\n' "$(tr -d '\r\n ' < .cf-token)" > scratch/auth.txt
|
|
55
|
+
curl -s https://api.cloudflare.com/client/v4/user/tokens/verify -H @scratch/auth.txt
|
|
56
|
+
curl -s https://api.cloudflare.com/client/v4/zones -H @scratch/auth.txt
|
|
57
|
+
curl -s https://api.cloudflare.com/client/v4/accounts -H @scratch/auth.txt
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
`"status": "active"`, the zones the token sees (the domain among them, or none yet), and the account with its id; the last call is refused without Account Settings Read, which is fine when the zone exists and carries the id. The file carries a carriage return on Windows; the `tr` strips it. These calls prove the token is live, not what it may change: Read lists exactly what Edit lists.
|
|
60
|
+
`"status": "active"`, the zones the token sees (the domain among them, or none yet), and the account with its id; the last call is refused without Account Settings Read, which is fine when the zone exists and carries the id. The file carries a carriage return on Windows; the `tr` strips it, and the header goes to `curl` from a file in `scratch/`, never on a command line. These calls prove the token is live, not what it may change: Read lists exactly what Edit lists.
|
|
61
61
|
|
|
62
62
|
## The git host
|
|
63
63
|
|
|
64
64
|
**The deployment repository.** The organization's deployment lives in a private repository on GitHub: the task definitions, the workflow that builds and deploys, the record. The agent makes it with the `gh` command signed in (`gh auth status`; `gh auth login` otherwise, which opens the browser for the person) under the owner the person names: their account, or an organization. Without `gh`, the person creates an empty private repository in the browser (New repository, private, no README) and gives its address, and the agent pushes to it.
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
### The GitHub App, for signing in with GitHub, and for the organization to say who is in
|
|
67
|
+
|
|
68
|
+
One app does both ([ADR-0022](/docs/adr/0022-who-is-in-is-the-consoles-own-record-and-an-administrator-keeps-it.md)): its client signs people in, and, installed on the organization, it lets the console read who is a member, at sign-in and every ten minutes after. Once the console's address is known, the person, signed in to GitHub as an owner of the organization (checked 2026-10): Your organizations, Settings next to the organization, Developer settings, GitHub Apps, New GitHub App.
|
|
67
69
|
|
|
68
70
|
- GitHub App name: the board's name (34 characters at most, unique across GitHub).
|
|
69
71
|
- Homepage URL: the console's address, `https://<hostname>`.
|
|
@@ -71,12 +73,14 @@ curl -s https://api.cloudflare.com/client/v4/accounts -H "Authorization: Bearer
|
|
|
71
73
|
- Webhook: untick Active. The console takes no webhooks.
|
|
72
74
|
- Permissions: under Organization permissions, Members: Read-only. Nothing else, and no repository permission: the console reads who is in, and nothing of the code.
|
|
73
75
|
- Where can this GitHub App be installed?: Only on this account.
|
|
74
|
-
- Create GitHub App. The app's page shows the **App ID** and the **Client ID**: copy both into the chat (neither is secret). Under Client secrets, Generate a new client secret, and save it to the secrets file as `GITHUB_CLIENT_SECRET` (below), as the line the agent shows. Under Private keys, Generate a private key: a `.pem` file downloads;
|
|
76
|
+
- Create GitHub App. The app's page shows the **App ID** and the **Client ID**: copy both into the chat (neither is secret). Under Client secrets, Generate a new client secret, and save it to the secrets file as `GITHUB_CLIENT_SECRET` (below), as the line the agent shows. Under Private keys, Generate a private key: a `.pem` file downloads; tell the agent where the file is, and it writes the key into the secrets file as `GITHUB_APP_PRIVATE_KEY` whole and deletes the file on the person's word; by hand instead, the file's lines joined with `\n`, as the line the agent shows. A secret pasted into the chat instead is written into the file by the agent's file tool and never repeated, and the person generates a new one once the run is over, since the chat kept a copy; the new one goes in as "a secret rotated" of the record.
|
|
75
77
|
- Install App, in the app's left sidebar: Install next to the organization, and confirm. No repository access is asked for, since the app has no repository permission. Everyone of the organization is in from then on, as the organization has them; the plan's `GITHUB_ORG` is the organization's login, as it reads in `github.com/<login>`.
|
|
76
78
|
|
|
77
79
|
Without an organization, an OAuth app signs people in as well (Developer settings, OAuth Apps, New OAuth App: the same name, homepage and callback; Register application; the Client ID and a client secret), and an administrator lets each person in on the People page. No scopes are needed either way: the console asks only who the person is.
|
|
78
80
|
|
|
79
|
-
|
|
81
|
+
### The OAuth client, for signing in with Google
|
|
82
|
+
|
|
83
|
+
In the Google Cloud console, a project of the organization's, Google Auth Platform (checked 2026-10): if the project has not registered with Google Auth yet, the Overview asks for the app's name, a support address and the audience (Internal, for a Workspace organization), once. Then Clients, Create client, Application type **Web application**, name it, under Authorized redirect URIs add `https://<hostname>/auth/google/callback`, Create. Copy the **Client ID** into the chat; the client secret is shown once: save it to the secrets file as `GOOGLE_CLIENT_SECRET`, as the line the agent shows (pasted instead: written to the file by the agent, never repeated, and replaced after the run, as with GitHub's). The Workspace domain, when everyone of it is a member, is the plan's `GOOGLE_WORKSPACE_DOMAIN`. Changes take from minutes to hours to be effective at Google.
|
|
80
84
|
|
|
81
85
|
## The secrets file
|
|
82
86
|
|
|
@@ -9,13 +9,13 @@ The run's folder is the deployment repository to be; `scratch/` in it holds the
|
|
|
9
9
|
```bash
|
|
10
10
|
export MSYS_NO_PATHCONV=1 # Git Bash on Windows: keep /console and the like as written
|
|
11
11
|
export AWS_PROFILE=console AWS_REGION=<region>
|
|
12
|
-
|
|
13
|
-
cf() { curl -sS "https://api.cloudflare.com/client/v4$1" -H
|
|
12
|
+
printf 'Authorization: Bearer %s\n' "$(tr -d '\r\n ' < .cf-token)" > scratch/auth.txt # the header from a file, never on a command line
|
|
13
|
+
cf() { curl -sS "https://api.cloudflare.com/client/v4$1" -H @scratch/auth.txt -H "Content-Type: application/json" "${@:2}"; }
|
|
14
14
|
ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
|
|
15
15
|
SECRETS=../console-secrets.json # outside the repository; made by the first `set` below, never a copy of assets/secrets.example.json
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Every id the run makes or finds (`CF_ACCOUNT`, `ZONE`, `VPC`, `SUBNETS`, `TASKS_SG`, `DB_SG`, `ENDPOINT`, `BUCKET`, `TUNNEL`, the rules' ids) goes into `scratch/notes.md` as it is made, as a line `NAME=value`, and a new shell sets them again from there (`set -a; . scratch/notes.md; set +a` when the file holds nothing but such lines): phases 5 to 7 come after a ten-minute watch, or after a pause of hours, almost always in a new shell.
|
|
18
|
+
Every id the run makes or finds (`CF_ACCOUNT`, `ZONE`, `VPC`, `SUBNETS`, `TASKS_SG`, `DB_SG`, `ENDPOINT`, `BUCKET`, `TUNNEL`, the rules' ids) goes into `scratch/notes.md` as it is made, as a line `NAME=value`, and a new shell sets them again from there (`set -a; . scratch/notes.md; set +a` when the file holds nothing but such lines): phases 5 to 7 come after a ten-minute watch, or after a pause of hours, almost always in a new shell. Where every command runs in a new shell, as some agents' tools do, the lines above go in `scratch/env.sh`, sourced at the start of every command; a wait longer than one command may take (the database, the workflow's run, a sign-in) runs in the background or is polled.
|
|
19
19
|
|
|
20
20
|
A value that must not be seen goes through a file or a pipe, never a command line: `aws ... --cli-input-json file://scratch/<file>` for a request that carries one, and the two scripts of this skill ([import-aws-key.mjs](../scripts/import-aws-key.mjs), [store-secrets.mjs](../scripts/store-secrets.mjs)), copied to `scratch/`, for the key and the parameters. A response that carries one is piped straight into the secrets file:
|
|
21
21
|
|
|
@@ -30,8 +30,8 @@ node -e "process.stdout.write(require('node:crypto').randomBytes(36).toString('b
|
|
|
30
30
|
2. The command and the scripts: `npm install -g @skillcdn/console`, then copy `$(npm root -g)/@skillcdn/console/skills/deploying-a-console/scripts/*.mjs` into `scratch/` (or write them there from the connection's copy of the skill).
|
|
31
31
|
3. The key: the person saved `.aws-key.csv` in the folder ([access.md](access.md)). `node scratch/import-aws-key.mjs .aws-key.csv console` writes the profile and deletes the file. Then `aws configure set region <region> --profile console`.
|
|
32
32
|
4. `aws sts get-caller-identity`: the account id and `user/console-deployer`. A refusal means the key is wrong or not yet active (a minute after creation).
|
|
33
|
-
5. The network: `aws ec2 describe-vpcs --filters Name=is-default,Values=true --query 'Vpcs[0].VpcId' --output text`. `None`:
|
|
34
|
-
6. The token: `cf /user/tokens/verify` answers `"status": "active"`; `cf "/zones?name=<domain>" > scratch/zone.json` the zone, with its id, its `status` and its account (`ZONE=$(jq -r '.result[0].id' scratch/zone.json)`, `CF_ACCOUNT=$(jq -r '.result[0].account.id' scratch/zone.json)`), or an empty list when the domain is not on Cloudflare yet, in which case `CF_ACCOUNT=$(cf /accounts | jq -r '.result[0].id')` and
|
|
33
|
+
5. The network: `aws ec2 describe-vpcs --filters Name=is-default,Values=true --query 'Vpcs[0].VpcId' --output text`. `None`: phase 3 "The network" makes one. Then the subnets, once there is a VPC: `aws ec2 describe-subnets --filters Name=vpc-id,Values=<vpc> Name=default-for-az,Values=true --query 'Subnets[].SubnetId' --output text`.
|
|
34
|
+
6. The token: `cf /user/tokens/verify` answers `"status": "active"`; `cf "/zones?name=<domain>" > scratch/zone.json` the zone, with its id, its `status` and its account (`ZONE=$(jq -r '.result[0].id' scratch/zone.json)`, `CF_ACCOUNT=$(jq -r '.result[0].account.id' scratch/zone.json)`), or an empty list when the domain is not on Cloudflare yet, in which case `CF_ACCOUNT=$(cf /accounts | jq -r '.result[0].id')` and the zone is made now, as "The zone, first" says: the one thing made before the plan, since it costs nothing, is undone as easily, and its nameservers must reach the person in the second message. A zone that exists: `cf "/zones/$ZONE/dns_records" | jq '.result | length'`; records of its own mean the zone serves the organization's site already, which the access record says and phase 3 "A zone that serves other hosts" acts on.
|
|
35
35
|
7. The git host: `gh auth status`; the owner the person named is theirs or an organization they are in (`gh api user`, `gh api user/orgs`).
|
|
36
36
|
8. The tools: `node --version` (24 or newer), `git --version`, `jq --version`, `curl --version`.
|
|
37
37
|
|
|
@@ -39,7 +39,7 @@ Report what is missing in one message, with the row of access.md to add, before
|
|
|
39
39
|
|
|
40
40
|
## Phase 2: Plan and estimate
|
|
41
41
|
|
|
42
|
-
Nothing is created. The size from the answer and its numbers ([sizing.md](sizing.md) "The sizes"); the prices read and the estimate computed ("Reading today's prices"); the names:
|
|
42
|
+
Nothing is created, but for the zone of phase 1, which costs nothing. The size from the answer and its numbers ([sizing.md](sizing.md) "The sizes"); the prices read and the estimate computed ("Reading today's prices"); the names:
|
|
43
43
|
|
|
44
44
|
| Resource | Name |
|
|
45
45
|
|---|---|
|
|
@@ -59,10 +59,12 @@ The plan message is the estimate message of sizing.md followed by the line in th
|
|
|
59
59
|
|
|
60
60
|
### The zone, first
|
|
61
61
|
|
|
62
|
-
Not on Cloudflare yet: `cf /zones -X POST --data '{"name":"<domain>","account":{"id":"'$CF_ACCOUNT'"},"type":"full"}'`, then `ZONE=$(
|
|
62
|
+
Not on Cloudflare yet, and not made in phase 1: `cf /zones -X POST --data '{"name":"<domain>","account":{"id":"'$CF_ACCOUNT'"},"type":"full"}' > scratch/zone.json`, then `ZONE=$(jq -r .result.id scratch/zone.json)`, and the nameservers are `jq -r '.result.name_servers[]' scratch/zone.json`. Before the nameservers change, the zone must carry what the domain serves already (its site, its mail), or that stops working at the switch: `cf "/zones/$ZONE/dns_records/scan" -X POST` has Cloudflare find the common records and add them (checked 2026-10), `cf "/zones/$ZONE/dns_records" | jq -r '.result[] | "\(.type) \(.name) \(.content)"'` lists what the zone now has, and the list goes to the person with the nameserver steps: "Compare this with the DNS page at your registrar and send me anything that is missing", each missing one added with `cf "/zones/$ZONE/dns_records" -X POST --data '{"type":"<MX | A | CNAME | TXT>","name":"<name>","content":"<value>","priority":<MX only>,"ttl":1}'`. Each record the scan added is checked for `proxied` (`cf "/zones/$ZONE/dns_records" | jq -r '.result[] | "\(.id) \(.name) \(.proxied)"'`): a record of the organization's site or mail is set `"proxied":false` (`cf "/zones/$ZONE/dns_records/<id>" -X PATCH --data '{"proxied":false}'`), so that it comes over as it is and nothing of the site passes through Cloudflare until the person asks for that; and the zone's SSL mode (`cf "/zones/$ZONE/settings/ssl"`) is read and said at the plan, since a site proxied later needs `full` or `strict` to keep serving its own certificate. The answer's `name_servers` go to the person with the steps for their registrar (access.md "The domain"), the run goes on, and the hostname answers once `cf "/zones?name=<domain>"` says `"status": "active"`. A domain to buy: `cf "/accounts/$CF_ACCOUNT/registrar/domain-search?q=<words>"` to find one, `cf "/accounts/$CF_ACCOUNT/registrar/domain-check" -X POST --data '{"domains":["<domain>"]}'` for the live price, the checkpoint with the price and the renewal, then `cf "/accounts/$CF_ACCOUNT/registrar/registrations" -X POST --data '{"domain_name":"<domain>"}'`; a `202` is polled at `.../registrations/<domain>/registration-status`. Buying through the API was in beta when checked (2026-10) and is not open to every account: `extension_not_supported_via_api`, a `404` or a route error means the person buys in the dashboard (Domain Registration, Register Domains), with the price in front of them there. A bought domain's zone is active at once, and `ZONE` is read as above.
|
|
63
63
|
|
|
64
64
|
### The network
|
|
65
65
|
|
|
66
|
+
No default VPC (phase 1 found `None`): `aws ec2 create-default-vpc` first, free of charge, then its subnets as phase 1 reads them.
|
|
67
|
+
|
|
66
68
|
```sh
|
|
67
69
|
VPC=<vpc>; SUBNETS=<subnet ids, comma-separated>
|
|
68
70
|
TASKS_SG=$(aws ec2 create-security-group --group-name console-tasks --description "console tasks: no inbound" --vpc-id $VPC --query GroupId --output text)
|
|
@@ -213,7 +215,7 @@ In the run's folder, write:
|
|
|
213
215
|
```sh
|
|
214
216
|
CONSOLE_REPOSITORY=https://github.com/skillcdn/console.git
|
|
215
217
|
CONSOLE_REF=<the commit of the release tag pinned at the plan>
|
|
216
|
-
CLOUDFLARED_VERSION=<the connector's release,
|
|
218
|
+
CLOUDFLARED_VERSION=<the connector's newest release, which gh api repos/cloudflare/cloudflared/releases/latest --jq .tag_name prints>
|
|
217
219
|
AWS_ACCOUNT_ID=<account id>
|
|
218
220
|
AWS_REGION=<region>
|
|
219
221
|
ECR_REPOSITORY=console
|
|
@@ -225,7 +227,7 @@ WORKER_COUNT=<0 | 0 | 1>
|
|
|
225
227
|
PLATFORM=linux/amd64
|
|
226
228
|
```
|
|
227
229
|
|
|
228
|
-
`PLATFORM=linux/arm64` with `cpuArchitecture` `ARM64` in the task definitions when the organization's builds run on ARM runners (sizing.md "Options").
|
|
230
|
+
`PLATFORM=linux/arm64` with `cpuArchitecture` `ARM64` in the task definitions when the organization's builds run on ARM runners (sizing.md "Options the plan offers").
|
|
229
231
|
|
|
230
232
|
### `deploy/api.task.json`
|
|
231
233
|
|
|
@@ -316,7 +318,7 @@ name: Deploy the console
|
|
|
316
318
|
on:
|
|
317
319
|
push:
|
|
318
320
|
branches: [main]
|
|
319
|
-
paths: ["deploy/**", "console-ui/**", ".github/workflows/deploy.yml"]
|
|
321
|
+
paths: ["deploy/**", "console-ui/**", "plugins/**", ".github/workflows/deploy.yml"]
|
|
320
322
|
workflow_dispatch:
|
|
321
323
|
permissions:
|
|
322
324
|
id-token: write
|
|
@@ -356,6 +358,9 @@ jobs:
|
|
|
356
358
|
(cd console-ui && npm ci && npm run build)
|
|
357
359
|
printf 'FROM %s\nCOPY --chown=node:node console-ui/dist/ /app/web/\n' "$IMAGE" | docker build --platform "$PLATFORM" -t "$IMAGE" -f - .
|
|
358
360
|
fi
|
|
361
|
+
if [ -f plugins/package.json ]; then
|
|
362
|
+
printf 'FROM node:24-bookworm-slim AS plugins\nWORKDIR /plugins\nCOPY plugins/package.json plugins/package-lock.json ./\nRUN npm ci --omit=dev --ignore-scripts\nFROM %s\nCOPY --from=plugins --chown=node:node /plugins/ /app/plugins/\nENV PLUGINS_DIR=/app/plugins\n' "$IMAGE" | docker build --platform "$PLATFORM" -t "$IMAGE" -f - .
|
|
363
|
+
fi
|
|
359
364
|
docker push "$IMAGE"
|
|
360
365
|
echo "IMAGE=$IMAGE" >> "$GITHUB_ENV"
|
|
361
366
|
- name: Register the task definitions
|
|
@@ -431,11 +436,20 @@ aws iam put-role-policy --role-name console-github-deploy --policy-name console-
|
|
|
431
436
|
|
|
432
437
|
`scratch/deploy-policy.json`: `ecr:GetAuthorizationToken` on `*`; `ecr:BatchCheckLayerAvailability`, `ecr:InitiateLayerUpload`, `ecr:UploadLayerPart`, `ecr:CompleteLayerUpload`, `ecr:PutImage`, `ecr:BatchGetImage`, `ecr:DescribeImages` on `arn:aws:ecr:<region>:<account>:repository/console`; `ecs:RegisterTaskDefinition`, `ecs:DescribeTaskDefinition`, `ecs:RunTask`, `ecs:DescribeTasks`, `ecs:DescribeServices`, `ecs:CreateService`, `ecs:UpdateService`, `ecs:ListTasks` on `*`; `iam:PassRole` on the two task roles; `logs:DescribeLogStreams`, `logs:GetLogEvents`, `logs:FilterLogEvents` on `arn:aws:logs:<region>:<account>:log-group:/console:*`.
|
|
433
438
|
|
|
439
|
+
### The organization's own console
|
|
440
|
+
|
|
441
|
+
`console-ui/`, when the organization has a look of its own: a console made with building-a-console at the plan's look, depending on `@skillcdn/console` at the version of the release `CONSOLE_REF` pins, with a `package-lock.json` (the workflow runs `npm ci`), a `build` script that runs `vite build` into `dist/`, and a `vite.config.ts` that sends `/api` to `console serve` while it is built; the workflow builds it on every push and lays `console-ui/dist/` over the image's web root. Absent, the image's default pages serve.
|
|
442
|
+
|
|
443
|
+
### The plugins
|
|
444
|
+
|
|
445
|
+
`plugins/package.json` and `plugins/package-lock.json`, when the plan names a plugin: `dependencies` only, each at an exact version from the registry or a tag of a git host (`github:<owner>/<repo>#<tag>`), written with `npm install --save-exact --package-lock-only --ignore-scripts <package>@<version>` in `plugins/`, which installs nothing on the machine; no scripts, no other field that does anything. The workflow installs them into the image from the lockfile with no install scripts and sets `PLUGINS_DIR`, as [deploy/README.md "Plugins in the image"](/deploy/README.md#plugins-in-the-image) lays it out; the console reads them at boot, and the administrator enables each on the settings page once the console runs. Absent, the console has no plugins. A plugin that asks `storage:sql` needs the database user to hold `CREATEROLE`, which the master user of phase 3's database does; one that keeps files is bounded with `PLUGIN_FILE_BYTES_AT_MOST` in `deploy/console.env` as `PROJECT_FILE_BYTES_AT_MOST` is, and each plugin's thread may hold `PLUGIN_MEMORY_MB_AT_MOST` (256 unless set), which the task's memory leaves room for.
|
|
446
|
+
|
|
434
447
|
### The rest of the repository
|
|
435
448
|
|
|
436
449
|
- `README.md`, in the organization's language: what this repository is (the deployment of the organization's console), that a push to `main` deploys, where the record is, and where the skill that made it lives (`skillcdn.ai/gh/skillcdn/console`).
|
|
437
450
|
- `DEPLOY.md`: the draft from the template (phase 8), filled as far as the run has gone.
|
|
438
451
|
- `console-ui/`: only when the organization has its own console (building-a-console makes it); otherwise absent and the image's default UI serves.
|
|
452
|
+
- `plugins/`: only when the plan names a plugin; otherwise absent, and the console has none.
|
|
439
453
|
|
|
440
454
|
### Commit, create, push, watch
|
|
441
455
|
|
|
@@ -542,6 +556,7 @@ Only on request, each step confirmed, in this order, after a last snapshot of th
|
|
|
542
556
|
| `aws rds create-db-instance` refuses the engine version | The region has no PostgreSQL 18 yet | `describe-db-engine-versions --engine postgres` and take the newest major it lists |
|
|
543
557
|
| `create-bucket` says the name is taken | Bucket names are global | Add a short suffix and update the task definition and the files policy |
|
|
544
558
|
| `POST /zones` is refused with a permission error | The token lacks Zone Edit | Add the row (access.md) and run the step again |
|
|
559
|
+
| `POST /zones` answers that the domain is on Cloudflare already, in another account | The organization's zone lives under another Cloudflare account | The person signs in there and moves the domain to this account, or makes the token under that account; the run then reads the zone as phase 1 does |
|
|
545
560
|
| The migration task exits `78` | A variable refused; the log names it, never its value | Fix the parameter or the environment entry, push again |
|
|
546
561
|
| The migration task exits `1` with a connection error | The tasks' group is not allowed into the database's, or the URL's host is wrong | Check the `authorize-security-group-ingress` and the endpoint in `DATABASE_URL` |
|
|
547
562
|
| `ResourceInitializationError` on a task: unable to pull secrets | The execution role cannot read `/console/*` | Check `console-parameters` on `console-task-execution` and the parameter ARNs |
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Running it again
|
|
2
|
+
|
|
3
|
+
A deployment is changed with the same skill, from phase 2: the plan names only what changes, with today's price where the change bills, the person says the word, the change is made as its row says, and the record, `DEPLOY.md`, is updated with it, in the same push or, where the change made none, in a push of its own, confirmed as phase 8 confirms it. The skill is read whole first, as for a first run; its rules hold here as there, and a push is a deployment (hard rule 3). The record's "How it is run" (resources.md "Phase 8: the record") says the same for the person who runs the console without an agent; the two are kept saying the same.
|
|
4
|
+
|
|
5
|
+
| What changes | What it takes |
|
|
6
|
+
|---|---|
|
|
7
|
+
| A new version of the console | The next release tag's commit as `CONSOLE_REF` in `deploy/console.env`, found as the Inputs say under "The console's version"; `console-ui/`, where there is one, moved to the same version of the package, so that the pages and the server stay one release; a push, which builds the image and rolls it, `migrate` first. |
|
|
8
|
+
| A plugin installed, updated or removed | The package named in `plugins/package.json` at an exact version or a tag of a git host, and the lockfile written beside it: `cd plugins && npm install --save-exact --package-lock-only --ignore-scripts <package>@<version>` (`npm uninstall --package-lock-only <package>` to remove), as [deploy/README.md "Plugins in the image"](/deploy/README.md#plugins-in-the-image) says; a push, which builds the image with the plugin installed and rolls it, `migrate` first. Then the administrator enables it on the console's settings page, consenting to what it asks, makes or chooses its board project and grants its roles ([planning-the-board](/skills/planning-the-board/references/setting-up.md)); a plugin with `storage:sql` needs the database user to hold `CREATEROLE`, which the master user does. The record names the plugin and its version. What a removed plugin stored stays until an administrator purges it. |
|
|
9
|
+
| A new connector | `CLOUDFLARED_VERSION` in `deploy/console.env`, the connector's newest release, which `gh api repos/cloudflare/cloudflared/releases/latest --jq .tag_name` prints; a push. |
|
|
10
|
+
| The next size | The numbers of the task definitions set to the next size's (sizing.md "The sizes"), `API_COUNT` and `WORKER_COUNT` in `deploy/console.env`; to `large`, `deploy/worker.task.json` written as resources.md gives it and `WORKER_IN_PROCESS` dropped from the api task; the database's class changed with one command, when nobody works (sizing.md "Growing"); the estimate of the new size at the plan; a push. |
|
|
11
|
+
| A person let in or taken out | With an organization at GitHub, in the organization, which the console reads on its own; otherwise by an administrator, on the People page or with `console person add <login>` and `console person deactivate <login>`; no push, since nothing of the deployment changes. |
|
|
12
|
+
| A secret rotated | The new value read from a secrets file of the same shape, as in phase 3, never from the chat; the parameter overwritten with the skill's script and the service rolled, so that the tasks start with it (resources.md "Phase 8: the record", "A secret rotated"); the file deleted again on the person's word; the record's line "a secret rotated", with the date. |
|
|
13
|
+
| A console shared with outside teams or customers, each in projects of their own | The settings of [deploy/README.md "A shared console"](/deploy/README.md#a-shared-console): who sees whom, the bytes a project's files may hold together, tokens that end; as lines of the task definitions, with a push, or on the Settings page, with none. |
|
|
14
|
+
| The console paused | Both services at zero tasks and the database stopped, which pauses the deployment at the cost of storage alone, for seven days at most, after which the database starts itself (sizing.md "Growing", "Fewer people"); the reverse resumes; the record says since when. |
|
|
15
|
+
| The console removed | A run of its own, asked for and confirmed resource by resource (hard rule 7), as resources.md "Removing" says: a last snapshot of the database first, and the deployment repository kept, with the record of what was removed. |
|
|
@@ -77,6 +77,8 @@ Part of the plan, in plain words, one line per item, the chosen size first:
|
|
|
77
77
|
|
|
78
78
|
## Options the plan offers
|
|
79
79
|
|
|
80
|
+
None of these is offered by default: the plan names one only when the answers call for it (files kept at Cloudflare already, no tunnel wanted, builds on ARM runners), and at `large` asks one question: "Should the database survive the loss of one data centre? Two zones double its line."
|
|
81
|
+
|
|
80
82
|
- **The database as serverless capacity.** Aurora Serverless v2 for PostgreSQL scales its capacity with load, down to nothing when idle (0 ACU with automatic pause, since 2024-11). The console holds a listening connection open and sweeps every fifteen minutes, so it never idles and the database never pauses: the floor is 0.5 ACU, about $44 a month at $0.12 per ACU-hour (read 2026-10), more than the `small` and `medium` instances. Offer it when the organization wants capacity that follows load without resizing, and say the floor.
|
|
81
83
|
- **The bucket on Cloudflare R2** instead of S3: the same S3 API, `S3_ENDPOINT=https://<account id>.r2.cloudflarestorage.com`, `S3_REGION=auto`, path style; no egress charges. Its keys are made in the Cloudflare dashboard (R2, Manage API tokens, Object Read & Write on the one bucket), saved in the secrets file as `S3_ACCESS_KEY_ID` and `S3_SECRET_ACCESS_KEY`, stored as parameters and carried by the `api` task as secrets, with no `S3_CREDENTIALS` line: the task's role cannot reach R2. Offer it when the organization keeps its files at Cloudflare already; S3 stays the default because the task's own role reaches it with no key to keep, and the budget and the audit are then in one account.
|
|
82
84
|
- **A load balancer instead of the tunnel.** ECS Express Mode creates an Application Load Balancer with HTTPS on an AWS domain in one command, at about $16 to $23 a month for the balancer (read 2026-10); the custom domain is a manual step on the balancer, and Cloudflare then proxies to it. Offer it only when the organization does not want its traffic through the tunnel; the tunnel is the default because it costs nothing, exposes nothing, and brings the client's address to the console cleanly.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: planning-the-board
|
|
3
|
+
description: "Puts work on the board of a SkillCDN Console and sets the board up, as a person asks, with the console command: tasks written from what they say or plan, each with its days, category, goal, parent and workers; goals on the roadmap; a project made or changed, with its mark, its members and its rules; the workspace's categories, people and settings; a private pack connected; a plugin's board project when an administrator enables one. Use when a person says 'put this on the board', 'write these as tasks', 'plan next week', 'set up a project for this', 'add Kim to the project', 'write the rules', 'make a category for design'. To work a task that is on the board, use working-the-board, which also writes a single piece of work given in chat before working it; to write a plugin, building-a-plugin. Everything is done as the person may: a task by anyone in the project, the rest by an owner or an administrator; what they may not, the console refuses, and the step goes back to them. Nothing is spent."
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Needs a shell with the console command on the PATH (npm install -g @skillcdn/console, Node.js 24 or newer), signed in by a person who works in the project, owns it, or administers the workspace, as the step needs. Nothing else; the agent never sees a token.
|
|
6
|
+
metadata:
|
|
7
|
+
author: skillcdn
|
|
8
|
+
version: "1.1"
|
|
9
|
+
skillcdn:
|
|
10
|
+
translations:
|
|
11
|
+
ko:
|
|
12
|
+
title: 보드 계획하고 설정하기
|
|
13
|
+
description: "SkillCDN 콘솔의 보드에 일을 올리고 보드를 설정합니다. 사람이 말하거나 계획한 일을 날짜·범주·목표·상위 작업·담당자와 함께 작업으로 쓰고, 로드맵에 목표를 세우고, 프로젝트를 만들거나 바꾸고(마크, 구성원, 규칙), 워크스페이스의 범주·사람·설정을 다루고, 비공개 팩을 연결하고, 관리자가 플러그인을 켤 때 그 보드 프로젝트를 만들거나 고릅니다. '이거 보드에 올려줘', '이것들 작업으로 써줘', '다음 주 계획 세워줘', '이 일로 프로젝트 만들어줘', '김OO 프로젝트에 추가해줘', '규칙 써줘', '디자인 범주 만들어줘', '연차 플러그인 켜줘'라고 할 때 쓰세요. 보드에 있는 작업을 하는 것은 working-the-board이고, 그 스킬도 채팅으로 받은 일 하나는 먼저 보드에 쓰고 시작합니다. 플러그인을 만드는 것은 building-a-plugin입니다. 모든 일은 그 사람이 할 수 있는 만큼 합니다: 작업은 프로젝트에서 일하는 누구나, 나머지는 프로젝트 소유자나 워크스페이스 관리자. 할 수 없는 일은 콘솔이 거부하고 그 단계는 사람에게 돌아갑니다. 비용은 들지 않습니다."
|
|
14
|
+
---
|
|
15
|
+
# Planning and setting up the board
|
|
16
|
+
|
|
17
|
+
What a person says goes in: work to put on the board, a plan for a week or a milestone, a project to set up, a member to add, a category, the rules. What comes out is the board changed as they said, and the numbers and the ids the commands printed, told back to them. The board is a SkillCDN Console ([architecture](/docs/architecture.md)): a workspace of projects, each a board of tasks with its roadmap of goals, sorted into the workspace's categories. The agent acts as the person who signed the command in, who works in the project, owns it or administers the workspace, and no further ([ADR-0021](/docs/adr/0021-a-token-configures-as-its-person-does.md)). Writing and setting up need no run: the project's feed of events records every write, with the agent named beside the person.
|
|
18
|
+
|
|
19
|
+
## How the person is involved
|
|
20
|
+
|
|
21
|
+
- **Questions:** only what the request and the board leave open, in one message before anything is read or written: the project, when the directory names none and the person has several ("Which project? `console projects` lists them by key."); a time the plan cannot turn into a day ("Which day is the launch?"; a week or a day named needs no question); a person named whom the project's members do not resolve to one login ("Which login is Kim? `console members` lists them."). Nothing else: the category, the goal, the parent, the state and the days are derived as the Inputs say, shown, and changed on request.
|
|
22
|
+
- **Checkpoints:** the plan before it is written, whenever more than one task is written, or a day, a category, a state or a worker was derived rather than said, or a goal is set or moved, or tasks that exist are changed: one message with the table of phase 2, the derived cells marked, and "OK" writes it. One task with everything said, a member, a category: written at once and told.
|
|
23
|
+
- **Go-ahead:** when the person says to go ahead alone, the plan is written at once and reported as the same table, with the numbers. The rules of a board are written only when the person asked for exactly that, go-ahead or not.
|
|
24
|
+
- **Changes mid-run:** a task written wrong is changed (`console task edit`), never written twice; one that should not exist is put away (`console task archive`), since nothing is deleted; a plan changed in the message is rewritten from that point and shown again.
|
|
25
|
+
|
|
26
|
+
## Requirements
|
|
27
|
+
|
|
28
|
+
| Need | How | When missing |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| The `console` command, signed in as the person | `console whoami` says as whom, at which console and in which project this directory works. `console projects` says what the person is in each project; `console people` and `console members` say who is who, by login. | Stop and tell the person: `npm install -g @skillcdn/console`, then `console login --url <the console's address>`, which shows an address and a code they approve on the console's own pages. Never look for a token. Refused because the person is out (`auth.deactivated`, `auth.not_admitted`, [ADR-0044](/docs/adr/0044-a-token-of-a-person-who-is-out-is-kept-and-refused-with-the-reason-until-they-are-back-in.md)), nothing is written, and the person is told. |
|
|
31
|
+
| The standing the step needs: what the person is in the project or the workspace | A task written, changed or put away: anyone who works in the project. A goal, the project's settings, its mark, its members, its rules, its pack token: an owner of the project (an administrator owns every project). A project made, the workspace's categories, people and settings, its rules and its pack token: an administrator ([ADR-0045](/docs/adr/0045-a-project-is-made-by-an-administrator.md)). | What the person may not, the console refuses (`auth.forbidden`): the step is told back to them with who may do it, and never worked around. |
|
|
32
|
+
| The command's own words | `console help <command>` says every form and flag, the limits (a title's length, how many tasks a project holds) and the refusals by code, read when the command is about to be used. | They are the reference at run time; this skill says what to write, and in what order. |
|
|
33
|
+
|
|
34
|
+
The board's words, task, goal, category, run, decision, page and project, are said once in [terms.md](/skills/working-the-board/references/terms.md); the command's contract is the [specification of the command line](/docs/specs/cli.md). A page this skill links by a path from the root (`/docs/...`, `/skills/...`) is a file of the console's repository: read it through the connection that served this skill, or in the package on this machine, where `console whoami` prints the skills are.
|
|
35
|
+
|
|
36
|
+
## Inputs
|
|
37
|
+
|
|
38
|
+
| Input | Source |
|
|
39
|
+
|---|---|
|
|
40
|
+
| The project | The one the directory works in (`console whoami`), else `--project <key>` on every command, from what the person said or the question above. |
|
|
41
|
+
| The work, or the plan | What the person said, in chat or in a document they point to: the pieces of work, their time, who does what. One piece of work is one task; a milestone that several tasks reach is a goal; a task waits for another only when the person said so, and a list is not an order. |
|
|
42
|
+
| The board as it is | `console categories`, the keys a task is sorted by; `console goals`, the roadmap; `console tasks --open` and `console tasks --search <words>`, so that a task is not written twice and a parent is found by its title; `console workspace`, the day the week begins and the time zone; `console rules`, which may say how tasks are written here: the language, the folders of the pages, who decides what. |
|
|
43
|
+
| The people | `console members` for the project, `console people` for the workspace, by login, which `--worker`, `member` and `person` take. A name is matched against the members' logins and names: exactly one match is the login, shown at the checkpoint; none, or several, is the question above. A login is matched as typed, so a person the list hides (where who sees whom is narrowed) is still named by their login. |
|
|
44
|
+
| The days | From the plan's words, by this machine's clock: `today`, `tomorrow`, `+7`, or a day as `2026-11-03`. A week named ("next week", "this week") is a start on its first day and a due on its last, the week beginning as `console workspace` says, and on Monday when it says `auto`; "by Friday" is a due day alone; a span the person gave in working days ends on its Friday only when they said so. A time that is not a day ("by the launch") is the question above. A due day is never before the start. |
|
|
45
|
+
| Everything else | Derived and marked in the plan: the category whose name the task's words name or imply, and when two fit alike the one the project's tasks of that kind use (`console tasks --search`), else left empty and named in the account; the state, `ready` when the task can be taken from today on, its start day being the plan and not a gate, and `idea` only when a decision or a fact the body lacks must come first; the priority, `normal` unless the person said otherwise; the goal from the milestone the person named, found with `console goals --search <words>`. |
|
|
46
|
+
|
|
47
|
+
## Workflow
|
|
48
|
+
|
|
49
|
+
The questions of "How the person is involved" are asked before phase 1 when the request leaves them open; after that, each phase ends with one line to the person, and only phase 2 stops for them.
|
|
50
|
+
|
|
51
|
+
### Phase 1: Read the board
|
|
52
|
+
|
|
53
|
+
Produces the **shape of the board**: the project, its categories, its goals, its open tasks, its members and its rules, read with the commands of the Inputs. A project that does not exist yet goes to phase 4 first, for an administrator. A category the plan needs and the workspace lacks is left empty on the task and proposed in the account, for an administrator to make; it is never made unasked.
|
|
54
|
+
|
|
55
|
+
### Phase 2: Shape the plan
|
|
56
|
+
|
|
57
|
+
Produces the **plan**: one line per task, as a table the person reads at a glance (title, category, start and due day, priority, goal, parent, workers, state, with the derived cells marked), and the goals to set or move. A task is one piece of work that one agent or person finishes and hands in. Its title says what is to be done, in plain words that stand on their own, up to 200 characters: a noun the person gave ("the pricing table") takes the plainest verb that makes it work to do ("Make the pricing table"), shown at the checkpoint, and the scope stays theirs; what the title refers to (a milestone, a commit, a page) goes in the body, linked where it can be. The body is the work order for the agent that takes the task, in the shape of [a-task.md](references/a-task.md), holding only what the person said: what to make in their words, where and the milestones when they said them, and under "Ask" what they did not say, so that the agent asks on the board rather than guesses; what the rules say is read first goes under "Read first". A task that exists already, one open task whose title names the same piece of work, is changed in what the person said of it (its days, its workers), not written again. Stop for the checkpoint as "How the person is involved" says.
|
|
58
|
+
|
|
59
|
+
### Phase 3: Write
|
|
60
|
+
|
|
61
|
+
Produces the **tasks and goals on the board**. A goal first, for an owner: `console goal new "<title>" [--body <markdown>] [--category <key>] [--starts <day>] [--due <day>]`, and `goal id:` is read from the line printed; a goal is changed with `console goal edit <id> [the same flags]`, `none` clearing a category or a day. Then each task: `console task new "<title>" --body <markdown>|--file <path> --state <state> [--priority <priority>] [--category <key>] [--goal <id>] [--starts <day>] [--due <day>] [--worker <login|me>]... [--parent <number|id>]`, `--state` given every time, since a task written without it is an `idea`, and a body longer than a line given with `--file` from a scratch file of the run. The number and the id are read from the lines printed (`Task #<n> written`, `task id:`), never assumed, since other agents write on the same board; `--json` prints the whole answer for a script. A change is `console task edit <number> [the same flags]`, `none` clearing a field; a task that should not exist is put away, `console task archive <number>`, and `console task restore <number>` brings it back. What the console refuses is named by its code and fixed or told back: `task.invalid_category`, `task.invalid_goal`, `task.invalid_dates`, `task.invalid_parent`, `task.too_many`; `task.invalid_worker` says the person may not work in the project, and listing them is a step of phase 4 the person asks for.
|
|
62
|
+
|
|
63
|
+
### Phase 4: Set up, when asked
|
|
64
|
+
|
|
65
|
+
Produces the **project and the workspace as asked**: a project made or changed, its mark, its members, the workspace's categories and people, its settings, the rules, a private pack connected, a plugin enabled with its board project, each with the command and the standing [setting-up.md](references/setting-up.md) gives it. A project's rules start from [project-rules.md](references/project-rules.md) and are written only when a person asked for exactly that. A plugin that puts decisions, tasks and pages on a board gets its project when an administrator enables it, made from the plugin's own defaults or chosen among those that exist (setting-up.md "A plugin's board project"); it is a project as any, its tasks planned here as any, and what the plugin writes there carries the plugin's name. What the person may not do goes back to them, with who may.
|
|
66
|
+
|
|
67
|
+
### Phase 5: Tell
|
|
68
|
+
|
|
69
|
+
Produces the **account**: the table of phase 2 as it stands on the board, with the numbers and the ids, and the console's address from `console whoami`; what was derived (a category, a day, a state, a login) and how to change it (`console task edit <number> ...`); what was refused, and whose step it is. In the person's language, in one message.
|
|
70
|
+
|
|
71
|
+
## Verdicts
|
|
72
|
+
|
|
73
|
+
| Result | Verdict |
|
|
74
|
+
|---|---|
|
|
75
|
+
| Every piece of work the person named is one task on the board, with the plan as they said it, no task twice, and the numbers told back | Done |
|
|
76
|
+
| A day, a category, a goal or a login could not be read from what was said: the task is written without it, and the account says so and how to set it | Done, with the gap named |
|
|
77
|
+
| A step was refused for the person's standing (`auth.forbidden`), a key was taken, a login was not of the workspace or not of the project | Told back, with who may do it or what to change; never worked around |
|
|
78
|
+
| A task was written twice, or in the wrong project | The extra one put away, and said so; nothing is deleted |
|
|
79
|
+
|
|
80
|
+
## Hard rules
|
|
81
|
+
|
|
82
|
+
- The plan is the person's: nothing on the board is changed that they did not ask for, and a task's days, category, goal and workers are set from their words by the rules of the Inputs, never from the agent's own view of what should come first.
|
|
83
|
+
- The board writes its moments itself: when a task was started and done is never typed. The state is set by hand only as the plan says (`idea`, `ready`, or `done` for work that was done elsewhere), and never around a run, which `console take` and `console finish` move.
|
|
84
|
+
- A number or an id is read from what the command printed, never assumed; a task whose number is not known is found with `console tasks --search <words>`, a goal's id with `console goals --search <words>`.
|
|
85
|
+
- Nothing is deleted: a task, a page and the rules are put away, and come back.
|
|
86
|
+
- A project is made by an administrator ([ADR-0045](/docs/adr/0045-a-project-is-made-by-an-administrator.md)), and a private one tells a member nothing of itself; a key does not change once made, so it is chosen with the person, and a key the console says is taken (`project.key_taken`) is not guessed around.
|
|
87
|
+
- The rules of a board are the people's: written only when a person asked for exactly that, from the example, and never to loosen what they ask of agents.
|
|
88
|
+
- Never look for, read, print or send a token. A pack token is connected with a code the person approves at SkillCDN, or read from standard input, never an argument.
|
|
89
|
+
- What the console refuses goes back to the person, in their words, with who may do it: the console decides on every request, and the agent tries no other way.
|
|
90
|
+
|
|
91
|
+
## Beside this skill
|
|
92
|
+
|
|
93
|
+
- The commands of setting up, with the standing each needs: [references/setting-up.md](references/setting-up.md).
|
|
94
|
+
- An example of a task written as a work order: [references/a-task.md](references/a-task.md).
|
|
95
|
+
- An example of a project's rules: [references/project-rules.md](references/project-rules.md).
|
|
96
|
+
- Working a task that is on the board: [working-the-board](/skills/working-the-board/SKILL.md).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# An example of a task
|
|
2
|
+
|
|
3
|
+
What a person, or their agent for them, writes on the board for an agent to take: the title says what is to be done and stands on its own; the flags carry the plan; the body is the work order, written from a scratch file of the run. An invented organization, Acme, and its web app, whose member `kim` is named by login. This is the full shape, for a person who gave the whole work order; a task given as a title alone keeps the same headings and says under "Ask" what was not said.
|
|
4
|
+
|
|
5
|
+
~~~sh
|
|
6
|
+
console task new "Refuse an empty file in the import parser" \
|
|
7
|
+
--file task.md --category engineering --goal <the goal's id> --due +3 --worker kim
|
|
8
|
+
~~~
|
|
9
|
+
|
|
10
|
+
`task.md`:
|
|
11
|
+
|
|
12
|
+
~~~markdown
|
|
13
|
+
## What to make
|
|
14
|
+
|
|
15
|
+
The parser at `src/import/parse.ts` accepts an empty file and writes an empty record. It should refuse one with the error `import.empty`, and the API should answer `400` with it.
|
|
16
|
+
|
|
17
|
+
## Where
|
|
18
|
+
|
|
19
|
+
The repository acme/web, a branch from `main`. Its AGENTS.md holds inside it: `pnpm check` before a hand-in, a pull request and never a push to main.
|
|
20
|
+
|
|
21
|
+
## Milestones
|
|
22
|
+
|
|
23
|
+
1. A failing test that imports an empty file.
|
|
24
|
+
2. The fix, with the error in the API's catalogue.
|
|
25
|
+
3. The pull request, handed in.
|
|
26
|
+
|
|
27
|
+
## Ask
|
|
28
|
+
|
|
29
|
+
Whether an empty file is refused or skipped is settled: refused. Ask if the fix needs a change to the schema, or touches another endpoint.
|
|
30
|
+
|
|
31
|
+
## Read first
|
|
32
|
+
|
|
33
|
+
`guides/local-setup` of this project's pages; the skill `engineering/skills/working-in-web/SKILL.md` of the pack.
|
|
34
|
+
~~~
|
|
35
|
+
|
|
36
|
+
The command prints the task's number and its id, which are told back to the person. The agent that takes the task reads the body with `console take`, works it as working-the-board says, and hands in the pull request. What was decided before (refused, not skipped) is in the body, so that no decision is asked for it; what is left open is named, so that the agent asks on the board rather than guessing.
|