@work-graph/cli 0.2.6 → 0.2.7
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/package.json +1 -1
- package/vendor/public/assets/img/work-graph-analytics-drawer.png +0 -0
- package/vendor/public/assets/img/work-graph-analytics-list.png +0 -0
- package/vendor/public/assets/img/work-graph-architecture-drawer.png +0 -0
- package/vendor/public/assets/img/work-graph-kanban-board-dark.png +0 -0
- package/vendor/public/assets/img/work-graph-kanban-board-light.png +0 -0
- package/vendor/public/assets/img/work-graph-memory-list.png +0 -0
- package/vendor/public/assets/img/work-graph-prompt-rules.png +0 -0
- package/vendor/public/assets/img/work-graph-task-drawer.png +0 -0
- package/vendor/public/assets/img/work-graph-verification-matrix.png +0 -0
- package/vendor/src/publicSiteServer.mjs +68 -17
- package/vendor/src/publicSiteStandaloneServer.mjs +97 -0
- package/vendor/src/workGraphBacklogUiServer.mjs +14 -7
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -88,6 +88,43 @@ function renderNav(locale, theme) {
|
|
|
88
88
|
</nav>`;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
const SCREENSHOTS = [
|
|
92
|
+
{
|
|
93
|
+
src: '/assets/img/work-graph-kanban-board-light.png',
|
|
94
|
+
title: { en: 'Kanban board', ru: 'Доска задач' },
|
|
95
|
+
body: { en: 'Local backlog columns with BVC work items and agent ownership.', ru: 'Локальная доска с BVC-задачами и владельцами.' },
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
src: '/assets/img/work-graph-analytics-list.png',
|
|
99
|
+
title: { en: 'Analytics list', ru: 'Аналитика' },
|
|
100
|
+
body: { en: 'Decision and research records connected to implementation work.', ru: 'Решения и исследования, связанные с задачами реализации.' },
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
src: '/assets/img/work-graph-task-drawer.png',
|
|
104
|
+
title: { en: 'Task contract drawer', ru: 'Контракт задачи' },
|
|
105
|
+
body: { en: 'Basis, Vector, Goal, analysis, decisions and evidence in one drawer.', ru: 'Базис, Вектор, Цель, анализ, решения и evidence в одной панели.' },
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
src: '/assets/img/work-graph-verification-matrix.png',
|
|
109
|
+
title: { en: 'Verification matrix', ru: 'Матрица проверок' },
|
|
110
|
+
body: { en: 'Deterministic, optional and environment-dependent gates before done.', ru: 'Детерминированные, опциональные и environment-гейты перед done.' },
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
src: '/assets/img/work-graph-architecture-drawer.png',
|
|
114
|
+
title: { en: 'Architecture drawer', ru: 'Архитектура' },
|
|
115
|
+
body: { en: 'Architecture blocks and derived projections for project navigation.', ru: 'Архитектурные блоки и производные проекции для навигации.' },
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
src: '/assets/img/work-graph-kanban-board-dark.png',
|
|
119
|
+
title: { en: 'Dark mode', ru: 'Тёмная тема' },
|
|
120
|
+
body: { en: 'The same local board in dark mode.', ru: 'Та же локальная доска в тёмной теме.' },
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
function screenshotText(value, locale) {
|
|
125
|
+
return value[locale] ?? value.en;
|
|
126
|
+
}
|
|
127
|
+
|
|
91
128
|
function renderSection(section, copy) {
|
|
92
129
|
const items = section.items
|
|
93
130
|
? `<ol class="flow-list">${section.items.map((item) => `<li>${escapeHtml(item)}</li>`).join('')}</ol>`
|
|
@@ -111,23 +148,29 @@ function renderSection(section, copy) {
|
|
|
111
148
|
}
|
|
112
149
|
|
|
113
150
|
function renderHeroVisual(locale) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
151
|
+
return `<figure class="template-visual screenshot-hero" aria-label="Work Graph board preview">
|
|
152
|
+
<img src="/assets/img/work-graph-kanban-board-light.png" alt="${escapeAttr(locale === 'ru' ? 'Доска Work Graph' : 'Work Graph kanban board')}" loading="eager" decoding="async">
|
|
153
|
+
<figcaption>${escapeHtml(locale === 'ru' ? 'Локальная доска Work Graph: backlog, ready, in progress and done.' : 'Local Work Graph board: backlog, ready, in progress and done.')}</figcaption>
|
|
154
|
+
</figure>`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function renderScreenshotGallery(locale) {
|
|
158
|
+
return `<section class="screenshot-gallery" aria-label="${escapeAttr(locale === 'ru' ? 'Скриншоты Work Graph' : 'Work Graph screenshots')}">
|
|
159
|
+
<div class="wide-heading">
|
|
160
|
+
<p class="eyebrow">${escapeHtml(locale === 'ru' ? 'Интерфейс' : 'Interface')}</p>
|
|
161
|
+
<h2>${escapeHtml(locale === 'ru' ? 'Как выглядит Work Graph' : 'What Work Graph looks like')}</h2>
|
|
162
|
+
<p>${escapeHtml(locale === 'ru' ? 'Реальные экраны локального UI: доска, аналитика, контракты задач, проверки и архитектура.' : 'Real local UI screens: board, analytics, task contracts, verification and architecture.')}</p>
|
|
121
163
|
</div>
|
|
122
|
-
<div class="
|
|
123
|
-
${
|
|
124
|
-
<
|
|
125
|
-
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
164
|
+
<div class="screenshot-grid">
|
|
165
|
+
${SCREENSHOTS.map((shot) => `<figure class="screenshot-card">
|
|
166
|
+
<img src="${escapeAttr(shot.src)}" alt="${escapeAttr(screenshotText(shot.title, locale))}" loading="lazy" decoding="async">
|
|
167
|
+
<figcaption>
|
|
168
|
+
<strong>${escapeHtml(screenshotText(shot.title, locale))}</strong>
|
|
169
|
+
<span>${escapeHtml(screenshotText(shot.body, locale))}</span>
|
|
170
|
+
</figcaption>
|
|
171
|
+
</figure>`).join('')}
|
|
129
172
|
</div>
|
|
130
|
-
</
|
|
173
|
+
</section>`;
|
|
131
174
|
}
|
|
132
175
|
|
|
133
176
|
function renderTemplateAside(copy, locale, theme) {
|
|
@@ -549,6 +592,8 @@ export function renderPublicSiteHtml(page, options = {}) {
|
|
|
549
592
|
.hero p { color: var(--muted); font-size: 1.125rem; line-height: 1.7; max-width: 820px; }
|
|
550
593
|
.cta-row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }
|
|
551
594
|
.template-visual { background: var(--card); border: 1px solid var(--border); border-radius: 4px; box-shadow: var(--shadow-raised); margin: 0 auto 56px; max-width: 1040px; overflow: hidden; }
|
|
595
|
+
.screenshot-hero img { display: block; height: auto; width: 100%; }
|
|
596
|
+
.screenshot-hero figcaption { border-top: 1px solid var(--border); color: var(--muted); font-size: 13px; padding: 12px 16px; }
|
|
552
597
|
.visual-toolbar { align-items: center; background: #fafbfc; border-bottom: 1px solid var(--border); display: flex; gap: 8px; padding: 12px 14px; }
|
|
553
598
|
.visual-toolbar span { background: var(--border); border-radius: 999px; height: 8px; width: 8px; }
|
|
554
599
|
.visual-board { background: #f4f5f7; display: grid; gap: 12px; grid-template-columns: repeat(4, 1fr); min-height: 310px; padding: 18px; }
|
|
@@ -587,6 +632,12 @@ export function renderPublicSiteHtml(page, options = {}) {
|
|
|
587
632
|
.proof-stats strong { color: var(--accent); display: block; font-size: clamp(1.8rem, 4vw, 3rem); letter-spacing: -.04em; line-height: 1; }
|
|
588
633
|
.proof-stats span { color: var(--muted); display: block; font-size: 13px; margin-top: 8px; }
|
|
589
634
|
.graph-trinity, .pillar-section, .install-section, .code-showcase, .audience-section, .comparison-strip, .roadmap-faq { margin: 58px auto 0; max-width: 1200px; }
|
|
635
|
+
.screenshot-gallery { margin: 58px auto 0; max-width: 1200px; }
|
|
636
|
+
.screenshot-grid { display: grid; gap: 18px; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 18px; }
|
|
637
|
+
.screenshot-card { background: var(--card); border: 1px solid var(--border); border-radius: 4px; box-shadow: var(--shadow); margin: 0; overflow: hidden; }
|
|
638
|
+
.screenshot-card img { display: block; height: auto; width: 100%; }
|
|
639
|
+
.screenshot-card figcaption { border-top: 1px solid var(--border); display: grid; gap: 4px; padding: 14px; }
|
|
640
|
+
.screenshot-card figcaption span { color: var(--muted); font-size: 13px; }
|
|
590
641
|
.wide-heading { max-width: 900px; }
|
|
591
642
|
.graph-flow { display: grid; gap: 18px; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-top: 20px; }
|
|
592
643
|
.graph-flow article { background: var(--card); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow); padding: 22px; position: relative; }
|
|
@@ -655,7 +706,7 @@ export function renderPublicSiteHtml(page, options = {}) {
|
|
|
655
706
|
.visual-board { grid-template-columns: repeat(2, 1fr); }
|
|
656
707
|
.content-layout { grid-template-columns: 1fr; }
|
|
657
708
|
.template-aside { position: static; }
|
|
658
|
-
.proof-stats, .graph-flow, .pillar-grid, .install-section, .code-showcase, .audience-section > div, .comparison-strip > div, .roadmap-faq, .related-grid, .footer-columns { grid-template-columns: 1fr; }
|
|
709
|
+
.proof-stats, .graph-flow, .pillar-grid, .install-section, .code-showcase, .audience-section > div, .comparison-strip > div, .roadmap-faq, .related-grid, .footer-columns, .screenshot-grid { grid-template-columns: 1fr; }
|
|
659
710
|
.graph-flow article + article::before { content: none; }
|
|
660
711
|
table { display: block; overflow-x: auto; white-space: nowrap; }
|
|
661
712
|
}
|
|
@@ -686,7 +737,7 @@ export function renderPublicSiteHtml(page, options = {}) {
|
|
|
686
737
|
${renderSteps(copy, locale)}
|
|
687
738
|
</div>
|
|
688
739
|
</div>
|
|
689
|
-
${page.kind === 'home' ? renderHomeExpansion(locale) : ''}`}
|
|
740
|
+
${page.kind === 'home' ? `${renderScreenshotGallery(locale)}${renderHomeExpansion(locale)}` : ''}`}
|
|
690
741
|
</article>
|
|
691
742
|
${renderRelatedTemplates(locale, theme)}
|
|
692
743
|
${renderBottomCta(copy, locale, theme)}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { createServer } from 'node:http';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
|
|
7
|
+
import { handlePublicSiteRequest } from './publicSiteServer.mjs';
|
|
8
|
+
import { resolveInstallLayout } from './workGraphInstallLayout.mjs';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_HOST = '127.0.0.1';
|
|
11
|
+
const DEFAULT_PORT = 4178;
|
|
12
|
+
|
|
13
|
+
const { PUBLIC_ROOT, DESIGN_TOKENS_WG_CSS_PATH } = resolveInstallLayout({ moduleUrl: import.meta.url });
|
|
14
|
+
|
|
15
|
+
function sendText(response, statusCode, body, contentType = 'text/plain') {
|
|
16
|
+
response.writeHead(statusCode, {
|
|
17
|
+
'content-type': `${contentType}; charset=utf-8`,
|
|
18
|
+
'cache-control': 'no-store',
|
|
19
|
+
});
|
|
20
|
+
response.end(body);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function serveFile(response, filePath, contentType) {
|
|
24
|
+
try {
|
|
25
|
+
const source = readFileSync(filePath);
|
|
26
|
+
response.writeHead(200, {
|
|
27
|
+
'content-type': contentType,
|
|
28
|
+
'cache-control': 'public, max-age=3600',
|
|
29
|
+
});
|
|
30
|
+
response.end(source);
|
|
31
|
+
} catch {
|
|
32
|
+
sendText(response, 404, 'not_found');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function serveAssetDir(url, response, rootDir, urlPrefix) {
|
|
37
|
+
if (!url.pathname.startsWith(urlPrefix)) return false;
|
|
38
|
+
const relativePath = decodeURIComponent(url.pathname.slice(urlPrefix.length));
|
|
39
|
+
if (!relativePath || relativePath.includes('..')) {
|
|
40
|
+
sendText(response, 403, 'forbidden');
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
const filePath = join(rootDir, relativePath);
|
|
44
|
+
if (!filePath.startsWith(rootDir)) {
|
|
45
|
+
sendText(response, 403, 'forbidden');
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
const contentType = filePath.endsWith('.svg')
|
|
49
|
+
? 'image/svg+xml; charset=utf-8'
|
|
50
|
+
: filePath.endsWith('.png')
|
|
51
|
+
? 'image/png'
|
|
52
|
+
: filePath.endsWith('.css')
|
|
53
|
+
? 'text/css; charset=utf-8'
|
|
54
|
+
: filePath.endsWith('.woff2')
|
|
55
|
+
? 'font/woff2'
|
|
56
|
+
: 'application/octet-stream';
|
|
57
|
+
serveFile(response, filePath, contentType);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function createPublicSiteServer() {
|
|
62
|
+
return createServer((request, response) => {
|
|
63
|
+
const url = new URL(request.url ?? '/', `http://${DEFAULT_HOST}`);
|
|
64
|
+
const method = request.method ?? 'GET';
|
|
65
|
+
if (method !== 'GET') {
|
|
66
|
+
sendText(response, 405, 'method_not_allowed');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (serveAssetDir(url, response, join(PUBLIC_ROOT, 'assets', 'img'), '/assets/img/')) return;
|
|
71
|
+
if (serveAssetDir(url, response, join(PUBLIC_ROOT, 'assets', 'icons'), '/assets/icons/')) return;
|
|
72
|
+
if (serveAssetDir(url, response, join(PUBLIC_ROOT, 'assets', 'avatars'), '/assets/avatars/')) return;
|
|
73
|
+
if (serveAssetDir(url, response, join(PUBLIC_ROOT, 'fonts'), '/assets/fonts/')) return;
|
|
74
|
+
|
|
75
|
+
if (url.pathname === '/assets/favicon.svg') {
|
|
76
|
+
serveFile(response, join(PUBLIC_ROOT, 'assets', 'favicon.svg'), 'image/svg+xml; charset=utf-8');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (url.pathname === '/assets/design-tokens-workgraph-dark.css') {
|
|
81
|
+
serveFile(response, DESIGN_TOKENS_WG_CSS_PATH, 'text/css; charset=utf-8');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (handlePublicSiteRequest(request, response, url)) return;
|
|
86
|
+
sendText(response, 404, 'not_found');
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
91
|
+
const host = process.env.WORKGRAPH_PUBLIC_SITE_HOST ?? DEFAULT_HOST;
|
|
92
|
+
const port = Number(process.env.WORKGRAPH_PUBLIC_SITE_PORT ?? DEFAULT_PORT);
|
|
93
|
+
const server = createPublicSiteServer();
|
|
94
|
+
server.listen(port, host, () => {
|
|
95
|
+
console.log(`Work Graph public site: http://${host}:${port}/`);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
import { buildCodeGapOperatorProjection } from './codeGapOperatorProjection.mjs';
|
|
19
19
|
import { buildEpicWorkScopeSlice } from './epicWorkScope.mjs';
|
|
20
20
|
import { renderUiKitPageHtml } from './ui/pages/uiKitPage.mjs';
|
|
21
|
-
import { handlePublicSiteRequest } from './publicSiteServer.mjs';
|
|
22
21
|
import { UI_BUTTON_CSS } from './ui/atoms/button.mjs';
|
|
23
22
|
import { UI_BADGE_CSS } from './ui/atoms/badge.mjs';
|
|
24
23
|
import { UI_SELECT_CSS } from './ui/atoms/select.mjs';
|
|
@@ -140,6 +139,13 @@ function tryServePublicAvatarsAsset(url, response) {
|
|
|
140
139
|
return tryServePublicAssetDir(url, response, join(PUBLIC_ROOT, 'assets', 'avatars'), '/assets/avatars/');
|
|
141
140
|
}
|
|
142
141
|
|
|
142
|
+
function tryServePublicImagesAsset(url, response) {
|
|
143
|
+
if (!url.pathname.startsWith('/assets/img/')) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
return tryServePublicAssetDir(url, response, join(PUBLIC_ROOT, 'assets', 'img'), '/assets/img/');
|
|
147
|
+
}
|
|
148
|
+
|
|
143
149
|
function tryServePublicAssetDir(url, response, rootDir, urlPrefix) {
|
|
144
150
|
const relativePath = decodeURIComponent(url.pathname.slice(urlPrefix.length));
|
|
145
151
|
if (!relativePath || relativePath.includes('..')) {
|
|
@@ -155,7 +161,9 @@ function tryServePublicAssetDir(url, response, rootDir, urlPrefix) {
|
|
|
155
161
|
const source = readFileSync(filePath);
|
|
156
162
|
const contentType = filePath.endsWith('.svg')
|
|
157
163
|
? 'image/svg+xml; charset=utf-8'
|
|
158
|
-
: '
|
|
164
|
+
: filePath.endsWith('.png')
|
|
165
|
+
? 'image/png'
|
|
166
|
+
: 'application/octet-stream';
|
|
159
167
|
response.writeHead(200, {
|
|
160
168
|
'content-type': contentType,
|
|
161
169
|
'cache-control': 'public, max-age=3600',
|
|
@@ -9878,10 +9886,6 @@ export function createBacklogUiServer(options = {}) {
|
|
|
9878
9886
|
const cwd = requestCtx.repoRoot;
|
|
9879
9887
|
const serverOptions = { cwd, backlogPath, journalPath, auditPath };
|
|
9880
9888
|
|
|
9881
|
-
if (handlePublicSiteRequest(request, response, url)) {
|
|
9882
|
-
return;
|
|
9883
|
-
}
|
|
9884
|
-
|
|
9885
9889
|
if (url.pathname === '/api/prompt-rules-projection' && method === 'GET') {
|
|
9886
9890
|
try {
|
|
9887
9891
|
const ruleId = url.searchParams.get('ruleId') ?? url.searchParams.get('id') ?? undefined;
|
|
@@ -10690,6 +10694,9 @@ export function createBacklogUiServer(options = {}) {
|
|
|
10690
10694
|
if (tryServePublicAvatarsAsset(url, response)) {
|
|
10691
10695
|
return;
|
|
10692
10696
|
}
|
|
10697
|
+
if (tryServePublicImagesAsset(url, response)) {
|
|
10698
|
+
return;
|
|
10699
|
+
}
|
|
10693
10700
|
|
|
10694
10701
|
if (url.pathname === '/favicon.ico' && method === 'GET') {
|
|
10695
10702
|
response.writeHead(204);
|
|
@@ -10772,7 +10779,7 @@ export function createBacklogUiServer(options = {}) {
|
|
|
10772
10779
|
return;
|
|
10773
10780
|
}
|
|
10774
10781
|
|
|
10775
|
-
if (url.pathname === '/app' || url.pathname === '/app/' || url.pathname === '/app/index.html') {
|
|
10782
|
+
if (url.pathname === '/' || url.pathname === '/app' || url.pathname === '/app/' || url.pathname === '/app/index.html') {
|
|
10776
10783
|
const locale = resolveUiLocale({
|
|
10777
10784
|
cookieHeader: request.headers.cookie,
|
|
10778
10785
|
acceptLanguage: request.headers['accept-language'],
|