@abstractframework/continuum 0.2.0

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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/bin/cli.js +206 -0
  4. package/bin/hub_proxy.js +549 -0
  5. package/dist/assets/abnfDiagram-VRR7QNED-DHZzhKO_.js +2 -0
  6. package/dist/assets/arc-9iPl5OLd.js +2 -0
  7. package/dist/assets/architectureDiagram-ZJ3FMSHR-BVRc0NRl.js +37 -0
  8. package/dist/assets/blockDiagram-677ZJIJ3-BTIe50OR.js +133 -0
  9. package/dist/assets/c4Diagram-LMCZKHZV-D5P-4VLr.js +11 -0
  10. package/dist/assets/channel-CEGeSBNz.js +2 -0
  11. package/dist/assets/chunk-2Q5K7J3B-WcmxYPWP.js +2 -0
  12. package/dist/assets/chunk-32BRIVSS-BPArWk_S.js +2 -0
  13. package/dist/assets/chunk-5VM5RSS4-DplW9bbf.js +16 -0
  14. package/dist/assets/chunk-EX3LRPZG-B8O0P99j.js +232 -0
  15. package/dist/assets/chunk-JWPE2WC7-DZoiE5m4.js +2 -0
  16. package/dist/assets/chunk-MOJQB5TN-FqMZZAe1.js +89 -0
  17. package/dist/assets/chunk-RYQCIY6F-D4I_kub_.js +2 -0
  18. package/dist/assets/chunk-V7JOEXUC-CwJZa6s3.js +207 -0
  19. package/dist/assets/chunk-VR4S4FIN-CdF7hCRc.js +2 -0
  20. package/dist/assets/chunk-XXDRQBXY-C2vm2DXm.js +2 -0
  21. package/dist/assets/classDiagram-OUVF2IWQ-D11Ru5dS.js +2 -0
  22. package/dist/assets/classDiagram-v2-EOCWNBFH-D11Ru5dS.js +2 -0
  23. package/dist/assets/cose-bilkent-JH36ORCC-BEoqSVoj.js +2 -0
  24. package/dist/assets/cynefin-VYW2F7L2-DzsDaKjB.js +179 -0
  25. package/dist/assets/cynefinDiagram-TSTJHNR4-qVt2ETEm.js +63 -0
  26. package/dist/assets/cytoscape.esm-DTSO7Bv0.js +332 -0
  27. package/dist/assets/dagre-VKFMJZFB-BszpuHqM.js +5 -0
  28. package/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
  29. package/dist/assets/diagram-FQU43EPY-DkSwN897.js +4 -0
  30. package/dist/assets/diagram-G47NLZAW-DmmkniMR.js +25 -0
  31. package/dist/assets/diagram-NH7WQ7WH-PkbaZ3fb.js +25 -0
  32. package/dist/assets/diagram-OA4YK3LP-D5O-VGkx.js +31 -0
  33. package/dist/assets/diagram-WEI45ONY-CtS1f8zZ.js +42 -0
  34. package/dist/assets/ebnfDiagram-CCIWWBDH-2ydDo0Sq.js +2 -0
  35. package/dist/assets/erDiagram-Q63AITRT-Dc-2Dmcc.js +86 -0
  36. package/dist/assets/flowDiagram-23GEKE2U-DKKOHSrp.js +157 -0
  37. package/dist/assets/ganttDiagram-NO4QXBWP-B9Zi-Zd_.js +293 -0
  38. package/dist/assets/gitGraphDiagram-IHSO6WYX-lXzgHn-s.js +107 -0
  39. package/dist/assets/graph-C9eacEi8.js +2 -0
  40. package/dist/assets/index-CCcufdYK.css +1 -0
  41. package/dist/assets/index-DmnZnFTc.js +124 -0
  42. package/dist/assets/infoDiagram-FWYZ7A6U-BHV_H3bL.js +3 -0
  43. package/dist/assets/init-Gi6I4Gst.js +2 -0
  44. package/dist/assets/ishikawaDiagram-FXEZZL3T-BX74rKth.js +71 -0
  45. package/dist/assets/journeyDiagram-5HDEW3XC-BByysGFJ.js +140 -0
  46. package/dist/assets/kanban-definition-HUTT4EX6-DatfLmCp.js +90 -0
  47. package/dist/assets/katex-C5jXJg4s.js +258 -0
  48. package/dist/assets/layout-DEXfKzaS.js +2 -0
  49. package/dist/assets/linear-BgTPNh4i.js +2 -0
  50. package/dist/assets/map-Czzmt4hB.js +2 -0
  51. package/dist/assets/mermaid.core-CrABX5SH.js +315 -0
  52. package/dist/assets/mindmap-definition-LN4V7U3C-Bz1XgdXA.js +97 -0
  53. package/dist/assets/ordinal-Cboi1Yqb.js +2 -0
  54. package/dist/assets/pegDiagram-2B236MQR-D5B8Egll.js +2 -0
  55. package/dist/assets/pieDiagram-ENE6RG2P-BaDg4f41.js +40 -0
  56. package/dist/assets/quadrantDiagram-ABIIQ3AL-jr1V5VXJ.js +8 -0
  57. package/dist/assets/railroadDiagram-RFXS5EU6-F13Ho8ra.js +2 -0
  58. package/dist/assets/requirementDiagram-TGXJPOKE-DXb_w8Ww.js +85 -0
  59. package/dist/assets/sankeyDiagram-HTMAVEWB-BN42kL5b.js +41 -0
  60. package/dist/assets/sequenceDiagram-DBY2YBRQ-DI7-dEyi.js +163 -0
  61. package/dist/assets/sizeCapture-X5ZJPWSS-Dv-15ctr.js +2 -0
  62. package/dist/assets/stateDiagram-2N3HPSRC-De5ObM42.js +2 -0
  63. package/dist/assets/stateDiagram-v2-6OUMAXLB-D9AwhP3S.js +2 -0
  64. package/dist/assets/swimlanes-5IMT3BWC-DzZwgXNi.js +3 -0
  65. package/dist/assets/swimlanesDiagram-G3AALYLV-DyzT80_E.js +9 -0
  66. package/dist/assets/timeline-definition-FHXFAJF6-BO8KkL_w.js +121 -0
  67. package/dist/assets/vennDiagram-L72KCM5P-D6LUv_CX.js +35 -0
  68. package/dist/assets/wardleyDiagram-EHGQE667-DWJp0Lmi.js +79 -0
  69. package/dist/assets/xychartDiagram-FW5EYKEG-B5IA4jNf.js +8 -0
  70. package/dist/index.html +14 -0
  71. package/package.json +68 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Laurent-Philippe Albou
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # AbstractContinuum
2
+
3
+ Continuous iterative development and deployment console for AbstractFramework.
4
+
5
+ Connect it to a Run Gateway and it becomes the framework's development
6
+ cockpit (board-first, kanban/scrum-inspired — see
7
+ [docs/design/redesign_2026_07.md](docs/design/redesign_2026_07.md)):
8
+
9
+ - **Board** (landing page) — the work flow as a kanban: Triage → Ready →
10
+ In Progress → In Review → Done, with a Failed history lane. Cards carry
11
+ type / priority (P0–P3) / labels (sprints ride the `sprint-N` label
12
+ convention) / package chips and a Definition-of-Ready dot; the card
13
+ drawer holds the spec (view/edit + metadata), the item's runs, and the
14
+ QA review (acceptance-criteria checklist → promote / iterate / UAT).
15
+ - **Executions** — the live ops view: queued / running / awaiting-QA
16
+ requests with live log tails (cursor-follow), one-click QA actions, and
17
+ a recently-finished strip.
18
+ - **Backlog** — the full catalog power view (all kinds incl. recurrent /
19
+ deprecated / trash, search, batch execute, merge, AI assist + advisor).
20
+ - **Agents & Entities** — the workforce: the configured execution agent
21
+ (executor is a pluggable seam — Codex CLI today), the advisor agent, a
22
+ track record from execution history, and gateway entities with their
23
+ skills.
24
+ - **Team** — an [agora](docs/configuration.md#team-page-agora-hub) hub
25
+ client for human + agent collaboration: channels and DMs with threaded
26
+ messages, a unified inbox of open asks, hub-wide search, channel files,
27
+ work claims, and live updates over WebSocket. It posts as one configured
28
+ operator seat through a server-side proxy (the seat key never reaches the
29
+ browser).
30
+ - **Inbox** — triage bug reports, feature requests, and email; run triage
31
+ and apply decisions.
32
+ - **Services** — control managed prod/UAT processes (start / stop /
33
+ restart / redeploy) and write-only environment variables. This surface is
34
+ high trust: see [SECURITY.md](SECURITY.md) before exposing it.
35
+ - **Settings** — gateway session sign-in and AI preferences.
36
+
37
+ Executing a Ready item is gated by an advisory **Definition of Ready**
38
+ checklist (type, real summary, acceptance criteria, test commands — parsed
39
+ from the spec, override always available and labeled); promoting from
40
+ review shows the acceptance criteria as a **Definition of Done** checklist.
41
+
42
+ The observer app ([AbstractObserver](https://github.com/lpalbou/AbstractObserver))
43
+ watches and discusses the running system; this app develops and deploys it.
44
+ The pipeline is executor-agnostic by design: the gateway currently runs
45
+ executions through the Codex CLI, but the executing agent is a pluggable
46
+ seam and the UI reads the executor identity from the gateway's exec config
47
+ (see [docs/architecture.md](docs/architecture.md)).
48
+
49
+ ## Install and run
50
+
51
+ Requires Node.js ≥ 18 and a running Run Gateway
52
+ ([AbstractGateway](https://github.com/lpalbou/AbstractGateway), default
53
+ `http://127.0.0.1:8080`).
54
+
55
+ ```bash
56
+ npx @abstractframework/continuum
57
+ # or install the CLI globally
58
+ npm install -g @abstractframework/continuum
59
+ abstractcontinuum
60
+ ```
61
+
62
+ Open `http://localhost:3002` and sign in with a gateway user and token (the
63
+ connection badge at the bottom of the sidebar). The token is exchanged
64
+ server-side for an HttpOnly session cookie and never stored in the browser.
65
+
66
+ Common environment variables (full list in
67
+ [docs/configuration.md](docs/configuration.md), or `abstractcontinuum --help`):
68
+
69
+ - `PORT` (default `3002`), `HOST` (default `127.0.0.1`)
70
+ - `ABSTRACTCONTINUUM_GATEWAY_URL` (or `ABSTRACTGATEWAY_URL`) — the gateway
71
+ this deployment talks to (default `http://127.0.0.1:8080`)
72
+ - `ABSTRACTCONTINUUM_HUB_URL` / `ABSTRACTCONTINUUM_HUB_SEAT` — the agora hub
73
+ and operator seat for the Team page
74
+
75
+ Before exposing the console beyond localhost, read
76
+ [docs/security.md](docs/security.md): a signed-in user can redeploy services.
77
+
78
+ ## Develop from source
79
+
80
+ The source build resolves the shared UI kit (`@abstractframework/ui-kit`,
81
+ `@abstractframework/panel-chat`) and the session proxy
82
+ (`@abstractframework/app-server`) from a sibling
83
+ [AbstractUIC](https://github.com/lpalbou/AbstractUIC) checkout:
84
+
85
+ ```bash
86
+ git clone https://github.com/lpalbou/AbstractUIC.git abstractuic
87
+ git clone https://github.com/lpalbou/AbstractContinuum.git abstractcontinuum
88
+ cd abstractcontinuum
89
+ npm install
90
+ npm run dev # vite dev server on :3003 (proxies /api to :8080)
91
+ npm test # vitest (unit + jsdom component tests)
92
+ npm run build # production build into dist/
93
+ npm start # serve dist/ + the app-origin gateway session proxy on :3002
94
+ ```
95
+
96
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow.
97
+
98
+ ## Documentation
99
+
100
+ - [docs/README.md](docs/README.md) — documentation index
101
+ - [docs/getting-started.md](docs/getting-started.md) — first run and first execution
102
+ - [docs/architecture.md](docs/architecture.md) — components, data flow, design boundaries
103
+ - [docs/api.md](docs/api.md) — the gateway API families this app consumes
104
+ - [docs/configuration.md](docs/configuration.md) — environment variables and settings
105
+ - [docs/security.md](docs/security.md) — trust model (read before deploying)
106
+ - [docs/faq.md](docs/faq.md) / [docs/troubleshooting.md](docs/troubleshooting.md)
107
+ - [CHANGELOG.md](CHANGELOG.md) — release history
108
+ - [CONTRIBUTING.md](CONTRIBUTING.md) — development workflow
109
+
110
+ ## License
111
+
112
+ MIT — see [LICENSE](LICENSE).
package/bin/cli.js ADDED
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * CLI entry point for AbstractContinuum
5
+ * Serves the built console and proxies /api/* to the gateway through the
6
+ * shared app-origin session proxy (first-party cookies + CSRF; tokens never
7
+ * in URLs) — the same @abstractframework/app-server module the observer,
8
+ * flow, and code apps converge on.
9
+ */
10
+
11
+ import * as http from 'http';
12
+ import { readFileSync, existsSync, statSync } from 'fs';
13
+ import { homedir } from 'os';
14
+ import { join, extname, dirname } from 'path';
15
+ import { fileURLToPath } from 'url';
16
+ import { createGatewaySessionProxy } from '@abstractframework/app-server';
17
+ import { createHubProxy } from './hub_proxy.js';
18
+
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ const DIST_DIR = join(__dirname, '..', 'dist');
21
+
22
+ const argv = process.argv.slice(2);
23
+ if (argv.includes('--version') || argv.includes('-v')) {
24
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
25
+ console.log(pkg.version);
26
+ process.exit(0);
27
+ }
28
+ if (argv.includes('--help') || argv.includes('-h')) {
29
+ console.log(`Usage: abstractcontinuum [--help] [--version]
30
+
31
+ Serves the AbstractContinuum console and proxies /api/* to a Run Gateway
32
+ through a same-origin session proxy. Configuration is environment-driven:
33
+
34
+ PORT HTTP port (default 3002)
35
+ HOST bind address (default 127.0.0.1)
36
+ ABSTRACTCONTINUUM_GATEWAY_URL gateway URL (fallback ABSTRACTGATEWAY_URL,
37
+ default http://127.0.0.1:8080)
38
+ ABSTRACTCONTINUUM_HUB_URL agora hub for the Team page (fallback
39
+ AGORA_HUB_URL, default http://127.0.0.1:8765)
40
+ ABSTRACTCONTINUUM_HUB_SEAT hub seat the Team page acts as (default laurent)
41
+ ABSTRACTCONTINUUM_HUB_KEYS hub key store (default ~/.agora/keys.json)
42
+ ABSTRACTCONTINUUM_HUB_KEY hub API key (overrides the key store)
43
+
44
+ Documentation: https://github.com/lpalbou/AbstractContinuum#readme`);
45
+ process.exit(0);
46
+ }
47
+
48
+ const PORT = process.env.PORT || 3002;
49
+ // Default bind is LOOPBACK (entity's c1768 SSRF finding against the shared
50
+ // session proxy, plus a continuum-specific amplifier: this server also
51
+ // mounts the hub proxy carrying the OPERATOR's seat key — a LAN peer
52
+ // reaching the port could author hub messages as the operator). Wider
53
+ // binds are an explicit deployment choice via HOST.
54
+ const HOST = process.env.HOST || '127.0.0.1';
55
+ const DEFAULT_GATEWAY_URL = String(process.env.ABSTRACTCONTINUUM_GATEWAY_URL || process.env.ABSTRACTGATEWAY_URL || 'http://127.0.0.1:8080').trim().replace(/\/+$/, '') || 'http://127.0.0.1:8080';
56
+ const HUB_URL = String(process.env.ABSTRACTCONTINUUM_HUB_URL || process.env.AGORA_HUB_URL || 'http://127.0.0.1:8765').trim().replace(/\/+$/, '');
57
+ const HUB_SEAT = String(process.env.ABSTRACTCONTINUUM_HUB_SEAT || 'laurent').trim();
58
+ const HUB_KEYS_PATH = String(process.env.ABSTRACTCONTINUUM_HUB_KEYS || join(homedir(), '.agora', 'keys.json'));
59
+
60
+ const MIME_TYPES = {
61
+ '.html': 'text/html',
62
+ '.js': 'application/javascript',
63
+ '.css': 'text/css',
64
+ '.json': 'application/json',
65
+ '.png': 'image/png',
66
+ '.jpg': 'image/jpeg',
67
+ '.jpeg': 'image/jpeg',
68
+ '.gif': 'image/gif',
69
+ '.svg': 'image/svg+xml',
70
+ '.ico': 'image/x-icon',
71
+ '.webmanifest': 'application/manifest+json',
72
+ };
73
+
74
+ function getMimeType(filePath) {
75
+ const ext = extname(filePath).toLowerCase();
76
+ return MIME_TYPES[ext] || 'application/octet-stream';
77
+ }
78
+
79
+ // Content-Security-Policy for the app document (security adversary P1
80
+ // 2026-07-16): untrusted markdown (channel files, messages, attachments)
81
+ // renders in this app, and standard markdown allows remote images — a
82
+ // crafted ![](https://attacker/beacon.png) would beacon the operator's IP
83
+ // and read-timing to an external host the moment a preview opens. img-src
84
+ // 'self' data: kills that class app-wide. script-src 'self' is a backstop
85
+ // belt (the renderer already cannot emit script). ws:/wss: are needed
86
+ // because 'self' does not reliably cover scheme-different WebSocket dials;
87
+ // only our own bundle can open sockets (script-src), so this stays tight.
88
+ const CSP = [
89
+ "default-src 'self'",
90
+ "script-src 'self'",
91
+ "style-src 'self' 'unsafe-inline'", // React style={} attributes
92
+ "img-src 'self' data:",
93
+ "font-src 'self' data:",
94
+ "connect-src 'self' ws: wss:",
95
+ "object-src 'none'",
96
+ "base-uri 'self'",
97
+ "form-action 'self'",
98
+ "frame-ancestors 'none'",
99
+ ].join('; ');
100
+
101
+ function serveFile(res, filePath) {
102
+ try {
103
+ if (!existsSync(filePath) || !statSync(filePath).isFile()) {
104
+ return false;
105
+ }
106
+ const content = readFileSync(filePath);
107
+ const mime = getMimeType(filePath);
108
+ const headers = {
109
+ 'Content-Type': mime,
110
+ 'Cache-Control': 'no-cache',
111
+ 'X-Content-Type-Options': 'nosniff',
112
+ };
113
+ // CSP is a document policy — attach it where a document loads.
114
+ if (mime === 'text/html') headers['Content-Security-Policy'] = CSP;
115
+ res.writeHead(200, headers);
116
+ res.end(content);
117
+ return true;
118
+ } catch {
119
+ return false;
120
+ }
121
+ }
122
+
123
+ // Cookies abstractcontinuum_gateway_*, header x-abstractcontinuum-csrf, and
124
+ // the ABSTRACTCONTINUUM_* / ABSTRACTGATEWAY_* env gates all derive from appId.
125
+ const gatewaySessionProxy = createGatewaySessionProxy({
126
+ appId: 'abstractcontinuum',
127
+ defaultGatewayUrl: DEFAULT_GATEWAY_URL,
128
+ });
129
+
130
+ // Team page transport: /api/hub/* forwards an allowlisted hub API subset
131
+ // with the OPERATOR seat's key attached server-side (proposal c1692,
132
+ // agency contract c1696 — authorship stays the operator's own).
133
+ const hubProxy = createHubProxy({ hubUrl: HUB_URL, seat: HUB_SEAT, keysPath: HUB_KEYS_PATH });
134
+
135
+ const server = http.createServer((req, res) => {
136
+ // Parse against a FIXED base: a malformed Host header (e.g. "a b") makes
137
+ // new URL throw synchronously, which would kill the whole process from
138
+ // one bad request (adversarial P1 2026-07-13). The host plays no role in
139
+ // routing here — only the pathname does.
140
+ let pathname = '/';
141
+ let search = '';
142
+ try {
143
+ const u = new URL(req.url, 'http://local');
144
+ pathname = u.pathname;
145
+ search = u.search;
146
+ } catch {
147
+ res.writeHead(400);
148
+ res.end('Bad Request');
149
+ return;
150
+ }
151
+
152
+ if (hubProxy.handle(req, res, pathname, search)) {
153
+ return;
154
+ }
155
+
156
+ if (gatewaySessionProxy.handle(req, res, pathname)) {
157
+ return;
158
+ }
159
+
160
+ if (pathname.includes('..')) {
161
+ res.writeHead(400);
162
+ res.end('Bad Request');
163
+ return;
164
+ }
165
+
166
+ if (pathname === '/' || pathname === '') {
167
+ pathname = '/index.html';
168
+ }
169
+
170
+ const filePath = join(DIST_DIR, pathname);
171
+ if (serveFile(res, filePath)) return;
172
+ if (serveFile(res, filePath + '.html')) return;
173
+
174
+ // SPA fallback
175
+ if (serveFile(res, join(DIST_DIR, 'index.html'))) return;
176
+
177
+ res.writeHead(404);
178
+ res.end('Not Found');
179
+ });
180
+
181
+ // Live hub updates: relay browser WebSockets to the hub with the seat key
182
+ // attached server-side (operator c2240 — realtime, not 5s polling).
183
+ server.on('upgrade', (req, socket, head) => {
184
+ // F7 (dm-99 tail): a raw upgrade socket with no 'error' listener is a
185
+ // process-killer (browser aborts mid-handshake raise uncaught 'error');
186
+ // and a rejected relay promise without .catch is an unhandled rejection.
187
+ socket.on('error', () => {
188
+ try { socket.destroy(); } catch { /* already gone */ }
189
+ });
190
+ void hubProxy.handleUpgrade(req, socket, head).then((handled) => {
191
+ if (!handled) socket.destroy();
192
+ }).catch(() => {
193
+ try { socket.destroy(); } catch { /* already gone */ }
194
+ });
195
+ });
196
+
197
+ server.listen(PORT, HOST, () => {
198
+ console.log(`
199
+ AbstractContinuum is running.
200
+ Local: http://localhost:${PORT}
201
+ Gateway: ${DEFAULT_GATEWAY_URL}
202
+ `);
203
+ });
204
+
205
+ process.on('SIGINT', () => process.exit(0));
206
+ process.on('SIGTERM', () => process.exit(0));