@planu/cli 0.35.2 → 0.37.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.
- package/dist/config/license-plans.json +2 -1
- package/dist/engine/docs-site-generator/dashboard-renderer.d.ts +3 -0
- package/dist/engine/docs-site-generator/dashboard-renderer.d.ts.map +1 -0
- package/dist/engine/docs-site-generator/dashboard-renderer.js +84 -0
- package/dist/engine/docs-site-generator/dashboard-renderer.js.map +1 -0
- package/dist/engine/docs-site-generator/data-collector.d.ts +5 -0
- package/dist/engine/docs-site-generator/data-collector.d.ts.map +1 -0
- package/dist/engine/docs-site-generator/data-collector.js +300 -0
- package/dist/engine/docs-site-generator/data-collector.js.map +1 -0
- package/dist/engine/docs-site-generator/diagrams-renderer.d.ts +2 -0
- package/dist/engine/docs-site-generator/diagrams-renderer.d.ts.map +1 -0
- package/dist/engine/docs-site-generator/diagrams-renderer.js +40 -0
- package/dist/engine/docs-site-generator/diagrams-renderer.js.map +1 -0
- package/dist/engine/docs-site-generator/html-templates.d.ts +12 -0
- package/dist/engine/docs-site-generator/html-templates.d.ts.map +1 -0
- package/dist/engine/docs-site-generator/html-templates.js +359 -0
- package/dist/engine/docs-site-generator/html-templates.js.map +1 -0
- package/dist/engine/docs-site-generator/index.d.ts +3 -0
- package/dist/engine/docs-site-generator/index.d.ts.map +1 -0
- package/dist/engine/docs-site-generator/index.js +74 -0
- package/dist/engine/docs-site-generator/index.js.map +1 -0
- package/dist/engine/docs-site-generator/markdown-renderer.d.ts +6 -0
- package/dist/engine/docs-site-generator/markdown-renderer.d.ts.map +1 -0
- package/dist/engine/docs-site-generator/markdown-renderer.js +168 -0
- package/dist/engine/docs-site-generator/markdown-renderer.js.map +1 -0
- package/dist/engine/docs-site-generator/spec-renderer.d.ts +4 -0
- package/dist/engine/docs-site-generator/spec-renderer.d.ts.map +1 -0
- package/dist/engine/docs-site-generator/spec-renderer.js +81 -0
- package/dist/engine/docs-site-generator/spec-renderer.js.map +1 -0
- package/dist/engine/docs-site-generator/tools-renderer.d.ts +3 -0
- package/dist/engine/docs-site-generator/tools-renderer.d.ts.map +1 -0
- package/dist/engine/docs-site-generator/tools-renderer.js +55 -0
- package/dist/engine/docs-site-generator/tools-renderer.js.map +1 -0
- package/dist/engine/safety/atomic-writer.d.ts +22 -0
- package/dist/engine/safety/atomic-writer.d.ts.map +1 -0
- package/dist/engine/safety/atomic-writer.js +89 -0
- package/dist/engine/safety/atomic-writer.js.map +1 -0
- package/dist/engine/safety/circuit-breaker.d.ts +14 -0
- package/dist/engine/safety/circuit-breaker.d.ts.map +1 -0
- package/dist/engine/safety/circuit-breaker.js +63 -0
- package/dist/engine/safety/circuit-breaker.js.map +1 -0
- package/dist/engine/safety/file-mutex.d.ts +13 -0
- package/dist/engine/safety/file-mutex.d.ts.map +1 -0
- package/dist/engine/safety/file-mutex.js +108 -0
- package/dist/engine/safety/file-mutex.js.map +1 -0
- package/dist/engine/safety/health-monitor.d.ts +4 -0
- package/dist/engine/safety/health-monitor.d.ts.map +1 -0
- package/dist/engine/safety/health-monitor.js +95 -0
- package/dist/engine/safety/health-monitor.js.map +1 -0
- package/dist/engine/safety/index.d.ts +7 -0
- package/dist/engine/safety/index.d.ts.map +1 -0
- package/dist/engine/safety/index.js +8 -0
- package/dist/engine/safety/index.js.map +1 -0
- package/dist/engine/safety/path-sanitizer.d.ts +17 -0
- package/dist/engine/safety/path-sanitizer.d.ts.map +1 -0
- package/dist/engine/safety/path-sanitizer.js +36 -0
- package/dist/engine/safety/path-sanitizer.js.map +1 -0
- package/dist/engine/safety/transaction.d.ts +18 -0
- package/dist/engine/safety/transaction.d.ts.map +1 -0
- package/dist/engine/safety/transaction.js +70 -0
- package/dist/engine/safety/transaction.js.map +1 -0
- package/dist/engine/webhook/server.d.ts +4 -10
- package/dist/engine/webhook/server.d.ts.map +1 -1
- package/dist/engine/webhook/server.js +78 -24
- package/dist/engine/webhook/server.js.map +1 -1
- package/dist/resources/process.js +2 -2
- package/dist/resources/process.js.map +1 -1
- package/dist/storage/base-store.d.ts.map +1 -1
- package/dist/storage/base-store.js +3 -2
- package/dist/storage/base-store.js.map +1 -1
- package/dist/storage/trash-store.d.ts +10 -0
- package/dist/storage/trash-store.d.ts.map +1 -1
- package/dist/storage/trash-store.js +74 -0
- package/dist/storage/trash-store.js.map +1 -1
- package/dist/tools/check-config-health.d.ts.map +1 -1
- package/dist/tools/check-config-health.js +2 -1
- package/dist/tools/check-config-health.js.map +1 -1
- package/dist/tools/generate-docs-site.d.ts +3 -0
- package/dist/tools/generate-docs-site.d.ts.map +1 -0
- package/dist/tools/generate-docs-site.js +25 -0
- package/dist/tools/generate-docs-site.js.map +1 -0
- package/dist/tools/generate-teammate-prompt.d.ts.map +1 -1
- package/dist/tools/generate-teammate-prompt.js +7 -4
- package/dist/tools/generate-teammate-prompt.js.map +1 -1
- package/dist/tools/plan-team-distribution.d.ts.map +1 -1
- package/dist/tools/plan-team-distribution.js +6 -2
- package/dist/tools/plan-team-distribution.js.map +1 -1
- package/dist/tools/register-agent-tools.d.ts.map +1 -1
- package/dist/tools/register-agent-tools.js +32 -14
- package/dist/tools/register-agent-tools.js.map +1 -1
- package/dist/tools/register-changelog-tools.d.ts.map +1 -1
- package/dist/tools/register-changelog-tools.js +6 -2
- package/dist/tools/register-changelog-tools.js.map +1 -1
- package/dist/tools/register-ci-tools.d.ts.map +1 -1
- package/dist/tools/register-ci-tools.js +12 -3
- package/dist/tools/register-ci-tools.js.map +1 -1
- package/dist/tools/register-context-tools.d.ts.map +1 -1
- package/dist/tools/register-context-tools.js +32 -11
- package/dist/tools/register-context-tools.js.map +1 -1
- package/dist/tools/register-coverage-tools.d.ts.map +1 -1
- package/dist/tools/register-coverage-tools.js +2 -1
- package/dist/tools/register-coverage-tools.js.map +1 -1
- package/dist/tools/register-delete-tools.d.ts.map +1 -1
- package/dist/tools/register-delete-tools.js +9 -8
- package/dist/tools/register-delete-tools.js.map +1 -1
- package/dist/tools/register-event-tools.d.ts.map +1 -1
- package/dist/tools/register-event-tools.js +69 -33
- package/dist/tools/register-event-tools.js.map +1 -1
- package/dist/tools/register-facilitator-tools.d.ts.map +1 -1
- package/dist/tools/register-facilitator-tools.js +10 -2
- package/dist/tools/register-facilitator-tools.js.map +1 -1
- package/dist/tools/register-governance-tools.d.ts.map +1 -1
- package/dist/tools/register-governance-tools.js +4 -1
- package/dist/tools/register-governance-tools.js.map +1 -1
- package/dist/tools/register-hooks-tools.d.ts.map +1 -1
- package/dist/tools/register-hooks-tools.js +45 -18
- package/dist/tools/register-hooks-tools.js.map +1 -1
- package/dist/tools/register-ide-tools.js +1 -1
- package/dist/tools/register-ide-tools.js.map +1 -1
- package/dist/tools/register-learning-tools.d.ts.map +1 -1
- package/dist/tools/register-learning-tools.js +7 -1
- package/dist/tools/register-learning-tools.js.map +1 -1
- package/dist/tools/register-license-tools.d.ts.map +1 -1
- package/dist/tools/register-license-tools.js +6 -1
- package/dist/tools/register-license-tools.js.map +1 -1
- package/dist/tools/register-llm-provider-tools.d.ts.map +1 -1
- package/dist/tools/register-llm-provider-tools.js +13 -9
- package/dist/tools/register-llm-provider-tools.js.map +1 -1
- package/dist/tools/register-memory-tools.d.ts.map +1 -1
- package/dist/tools/register-memory-tools.js +17 -6
- package/dist/tools/register-memory-tools.js.map +1 -1
- package/dist/tools/register-migration-tools.d.ts.map +1 -1
- package/dist/tools/register-migration-tools.js +8 -1
- package/dist/tools/register-migration-tools.js.map +1 -1
- package/dist/tools/register-orchestrator-tools.d.ts.map +1 -1
- package/dist/tools/register-orchestrator-tools.js +15 -14
- package/dist/tools/register-orchestrator-tools.js.map +1 -1
- package/dist/tools/register-platform-tools/design-stack-tools.d.ts.map +1 -1
- package/dist/tools/register-platform-tools/design-stack-tools.js +62 -35
- package/dist/tools/register-platform-tools/design-stack-tools.js.map +1 -1
- package/dist/tools/register-platform-tools/lifecycle-infra-tools.d.ts.map +1 -1
- package/dist/tools/register-platform-tools/lifecycle-infra-tools.js +72 -36
- package/dist/tools/register-platform-tools/lifecycle-infra-tools.js.map +1 -1
- package/dist/tools/register-readiness-tools.js +4 -4
- package/dist/tools/register-readiness-tools.js.map +1 -1
- package/dist/tools/register-runtime-security-tools.js +1 -1
- package/dist/tools/register-runtime-security-tools.js.map +1 -1
- package/dist/tools/register-scope-tools.d.ts.map +1 -1
- package/dist/tools/register-scope-tools.js +26 -6
- package/dist/tools/register-scope-tools.js.map +1 -1
- package/dist/tools/register-search-tools.js +2 -2
- package/dist/tools/register-search-tools.js.map +1 -1
- package/dist/tools/register-security-tools.d.ts.map +1 -1
- package/dist/tools/register-security-tools.js +4 -3
- package/dist/tools/register-security-tools.js.map +1 -1
- package/dist/tools/register-spec-tools/analysis-tools.d.ts.map +1 -1
- package/dist/tools/register-spec-tools/analysis-tools.js +36 -25
- package/dist/tools/register-spec-tools/analysis-tools.js.map +1 -1
- package/dist/tools/register-spec-tools/core-spec-tools.d.ts.map +1 -1
- package/dist/tools/register-spec-tools/core-spec-tools.js +38 -24
- package/dist/tools/register-spec-tools/core-spec-tools.js.map +1 -1
- package/dist/tools/register-stack-tools.d.ts.map +1 -1
- package/dist/tools/register-stack-tools.js +18 -10
- package/dist/tools/register-stack-tools.js.map +1 -1
- package/dist/tools/register-team-tools.d.ts.map +1 -1
- package/dist/tools/register-team-tools.js +17 -9
- package/dist/tools/register-team-tools.js.map +1 -1
- package/dist/tools/register-template-tools.d.ts.map +1 -1
- package/dist/tools/register-template-tools.js +13 -2
- package/dist/tools/register-template-tools.js.map +1 -1
- package/dist/tools/register-transform-tools.d.ts.map +1 -1
- package/dist/tools/register-transform-tools.js +3 -2
- package/dist/tools/register-transform-tools.js.map +1 -1
- package/dist/tools/register-workspace-tools.d.ts.map +1 -1
- package/dist/tools/register-workspace-tools.js +21 -1
- package/dist/tools/register-workspace-tools.js.map +1 -1
- package/dist/tools/schemas/github.d.ts.map +1 -1
- package/dist/tools/schemas/github.js +18 -9
- package/dist/tools/schemas/github.js.map +1 -1
- package/dist/tools/schemas/ide-config.js +1 -1
- package/dist/tools/schemas/ide-config.js.map +1 -1
- package/dist/tools/schemas/plugins-schemas.d.ts.map +1 -1
- package/dist/tools/schemas/plugins-schemas.js +15 -4
- package/dist/tools/schemas/plugins-schemas.js.map +1 -1
- package/dist/tools/schemas/registry.d.ts.map +1 -1
- package/dist/tools/schemas/registry.js +27 -10
- package/dist/tools/schemas/registry.js.map +1 -1
- package/dist/tools/schemas/session.d.ts.map +1 -1
- package/dist/tools/schemas/session.js +5 -3
- package/dist/tools/schemas/session.js.map +1 -1
- package/dist/tools/schemas/workers-schema.d.ts.map +1 -1
- package/dist/tools/schemas/workers-schema.js +16 -9
- package/dist/tools/schemas/workers-schema.js.map +1 -1
- package/dist/tools/validate-team-results.d.ts.map +1 -1
- package/dist/tools/validate-team-results.js +9 -3
- package/dist/tools/validate-team-results.js.map +1 -1
- package/dist/types/docs.d.ts +51 -0
- package/dist/types/docs.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/safe-schemas.d.ts +12 -0
- package/dist/types/safe-schemas.d.ts.map +1 -0
- package/dist/types/safe-schemas.js +27 -0
- package/dist/types/safe-schemas.js.map +1 -0
- package/dist/types/safety.d.ts +66 -0
- package/dist/types/safety.d.ts.map +1 -0
- package/dist/types/safety.js +3 -0
- package/dist/types/safety.js.map +1 -0
- package/package.json +1 -1
- package/src/config/license-plans.json +2 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { renderProgressBar, renderStatusBadge } from './html-templates.js';
|
|
2
|
+
import { renderMarkdown } from './markdown-renderer.js';
|
|
3
|
+
export function renderSpecPage(spec, specContent, technicalContent) {
|
|
4
|
+
const tagsHtml = spec.tags.length > 0
|
|
5
|
+
? spec.tags.map((t) => `<span class="tag">${t}</span>`).join(' ')
|
|
6
|
+
: '<span class="tag">none</span>';
|
|
7
|
+
return `<div class="spec-page">
|
|
8
|
+
<header class="spec-header">
|
|
9
|
+
<h1>${spec.id}: ${spec.title}</h1>
|
|
10
|
+
${renderStatusBadge(spec.status)}
|
|
11
|
+
${renderProgressBar(spec.criteriaDone, spec.criteriaTotal)}
|
|
12
|
+
</header>
|
|
13
|
+
<div class="spec-layout">
|
|
14
|
+
<div class="spec-content">
|
|
15
|
+
<div class="tabs">
|
|
16
|
+
<div class="tab active" id="tab-story">
|
|
17
|
+
<h2>User Story</h2>
|
|
18
|
+
${renderMarkdown(specContent)}
|
|
19
|
+
</div>
|
|
20
|
+
<div class="tab" id="tab-technical">
|
|
21
|
+
<h2>Technical Sheet</h2>
|
|
22
|
+
${renderMarkdown(technicalContent)}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
<aside class="spec-sidebar">
|
|
27
|
+
<h3>Metadata</h3>
|
|
28
|
+
<dl>
|
|
29
|
+
<dt>Type</dt><dd>${spec.type}</dd>
|
|
30
|
+
<dt>Category</dt><dd>${spec.category}</dd>
|
|
31
|
+
<dt>Tags</dt><dd>${tagsHtml}</dd>
|
|
32
|
+
<dt>Criteria</dt><dd>${String(spec.criteriaDone)} / ${String(spec.criteriaTotal)}</dd>
|
|
33
|
+
</dl>
|
|
34
|
+
</aside>
|
|
35
|
+
</div>
|
|
36
|
+
</div>`;
|
|
37
|
+
}
|
|
38
|
+
function groupByCategory(specs) {
|
|
39
|
+
const groups = {};
|
|
40
|
+
for (const spec of specs) {
|
|
41
|
+
const cat = spec.category || '_general';
|
|
42
|
+
const list = groups[cat];
|
|
43
|
+
if (list) {
|
|
44
|
+
list.push(spec);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
groups[cat] = [spec];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return groups;
|
|
51
|
+
}
|
|
52
|
+
export function renderSpecIndex(specs) {
|
|
53
|
+
if (specs.length === 0) {
|
|
54
|
+
return '<div class="spec-index"><p class="empty">No specs found.</p></div>';
|
|
55
|
+
}
|
|
56
|
+
const grouped = groupByCategory(specs);
|
|
57
|
+
const sections = Object.entries(grouped)
|
|
58
|
+
.map(([category, categorySpecs]) => {
|
|
59
|
+
const cards = categorySpecs
|
|
60
|
+
.map((spec) => {
|
|
61
|
+
const searchable = `${spec.id} ${spec.title}`.toLowerCase();
|
|
62
|
+
return ` <div class="spec-card" data-searchable="${searchable}">
|
|
63
|
+
<a href="${spec.slug}.html">
|
|
64
|
+
<strong>${spec.id}</strong>: ${spec.title}
|
|
65
|
+
${renderStatusBadge(spec.status)}
|
|
66
|
+
${renderProgressBar(spec.criteriaDone, spec.criteriaTotal)}
|
|
67
|
+
</a>
|
|
68
|
+
</div>`;
|
|
69
|
+
})
|
|
70
|
+
.join('\n');
|
|
71
|
+
return ` <section class="spec-category">
|
|
72
|
+
<h2>${category}</h2>
|
|
73
|
+
${cards}
|
|
74
|
+
</section>`;
|
|
75
|
+
})
|
|
76
|
+
.join('\n');
|
|
77
|
+
return `<div class="spec-index">
|
|
78
|
+
${sections}
|
|
79
|
+
</div>`;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=spec-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-renderer.js","sourceRoot":"","sources":["../../../src/engine/docs-site-generator/spec-renderer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,UAAU,cAAc,CAC5B,IAAoB,EACpB,WAAmB,EACnB,gBAAwB;IAExB,MAAM,QAAQ,GACZ,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QAClB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QACjE,CAAC,CAAC,+BAA+B,CAAC;IAEtC,OAAO;;UAEC,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,KAAK;MAC1B,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;MAC9B,iBAAiB,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,aAAa,CAAC;;;;;;;YAOlD,cAAc,CAAC,WAAW,CAAC;;;;YAI3B,cAAc,CAAC,gBAAgB,CAAC;;;;;;;2BAOjB,IAAI,CAAC,IAAI;+BACL,IAAI,CAAC,QAAQ;2BACjB,QAAQ;+BACJ,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;;;;OAIjF,CAAC;AACR,CAAC;AAED,SAAS,eAAe,CAAC,KAAuB;IAC9C,MAAM,MAAM,GAAqC,EAAE,CAAC;IACpD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,IAAI,UAAU,CAAC;QACxC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAuB;IACrD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,oEAAoE,CAAC;IAC9E,CAAC;IAED,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;SACrC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,EAAE;QACjC,MAAM,KAAK,GAAG,aAAa;aACxB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACZ,MAAM,UAAU,GAAG,GAAG,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC;YAC5D,OAAO,+CAA+C,UAAU;iBACzD,IAAI,CAAC,IAAI;kBACR,IAAI,CAAC,EAAE,cAAc,IAAI,CAAC,KAAK;UACvC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;UAC9B,iBAAiB,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,aAAa,CAAC;;WAEvD,CAAC;QACJ,CAAC,CAAC;aACD,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,OAAO;UACH,QAAQ;EAChB,KAAK;aACM,CAAC;IACV,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;EACP,QAAQ;OACH,CAAC;AACR,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools-renderer.d.ts","sourceRoot":"","sources":["../../../src/engine/docs-site-generator/tools-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAqBtD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,CAuC7D"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
function groupByPhase(tools) {
|
|
2
|
+
const groups = {};
|
|
3
|
+
for (const tool of tools) {
|
|
4
|
+
const list = groups[tool.phase];
|
|
5
|
+
if (list) {
|
|
6
|
+
list.push(tool);
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
groups[tool.phase] = [tool];
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return groups;
|
|
13
|
+
}
|
|
14
|
+
function renderTierBadge(tier) {
|
|
15
|
+
const cls = tier === 'free' ? 'badge-free' : 'badge-pro';
|
|
16
|
+
const label = tier === 'free' ? 'Free' : 'Pro';
|
|
17
|
+
return `<span class="tier-badge ${cls}">${label}</span>`;
|
|
18
|
+
}
|
|
19
|
+
export function renderToolsCatalog(tools) {
|
|
20
|
+
const freeCount = tools.filter((t) => t.tier === 'free').length;
|
|
21
|
+
const proCount = tools.filter((t) => t.tier === 'pro').length;
|
|
22
|
+
if (tools.length === 0) {
|
|
23
|
+
return `<div class="tools-catalog">
|
|
24
|
+
<div class="tools-summary">
|
|
25
|
+
<p>Total: 0 | Free: 0 | Pro: 0</p>
|
|
26
|
+
</div>
|
|
27
|
+
<p class="empty">No tools found.</p>
|
|
28
|
+
</div>`;
|
|
29
|
+
}
|
|
30
|
+
const grouped = groupByPhase(tools);
|
|
31
|
+
const sections = Object.entries(grouped)
|
|
32
|
+
.map(([phase, phaseTools]) => {
|
|
33
|
+
const cards = phaseTools
|
|
34
|
+
.map((tool) => {
|
|
35
|
+
const searchable = `${tool.name} ${tool.description}`.toLowerCase();
|
|
36
|
+
return ` <div class="tool-card" data-searchable="${searchable}">
|
|
37
|
+
<h3>${tool.name} ${renderTierBadge(tool.tier)}</h3>
|
|
38
|
+
<p>${tool.description}</p>
|
|
39
|
+
</div>`;
|
|
40
|
+
})
|
|
41
|
+
.join('\n');
|
|
42
|
+
return ` <section class="tools-phase">
|
|
43
|
+
<h2>${phase}</h2>
|
|
44
|
+
${cards}
|
|
45
|
+
</section>`;
|
|
46
|
+
})
|
|
47
|
+
.join('\n');
|
|
48
|
+
return `<div class="tools-catalog">
|
|
49
|
+
<div class="tools-summary">
|
|
50
|
+
<p>Total: ${String(tools.length)} | Free: ${String(freeCount)} | Pro: ${String(proCount)}</p>
|
|
51
|
+
</div>
|
|
52
|
+
${sections}
|
|
53
|
+
</div>`;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=tools-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools-renderer.js","sourceRoot":"","sources":["../../../src/engine/docs-site-generator/tools-renderer.ts"],"names":[],"mappings":"AAEA,SAAS,YAAY,CAAC,KAAkB;IACtC,MAAM,MAAM,GAAgC,EAAE,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,IAAoB;IAC3C,MAAM,GAAG,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC;IACzD,MAAM,KAAK,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/C,OAAO,2BAA2B,GAAG,KAAK,KAAK,SAAS,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAkB;IACnD,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IAChE,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,MAAM,CAAC;IAE9D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;;;;;OAKJ,CAAC;IACN,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;SACrC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,EAAE;QAC3B,MAAM,KAAK,GAAG,UAAU;aACrB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACZ,MAAM,UAAU,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,CAAC;YACpE,OAAO,+CAA+C,UAAU;YAC9D,IAAI,CAAC,IAAI,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;WACxC,IAAI,CAAC,WAAW;WAChB,CAAC;QACJ,CAAC,CAAC;aACD,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,OAAO;UACH,KAAK;EACb,KAAK;aACM,CAAC;IACV,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;;gBAEO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,MAAM,CAAC,SAAS,CAAC,WAAW,MAAM,CAAC,QAAQ,CAAC;;EAE1F,QAAQ;OACH,CAAC;AACR,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { WriteOptions } from '../../types/safety.js';
|
|
2
|
+
/**
|
|
3
|
+
* Write JSON data atomically with backup rotation.
|
|
4
|
+
*
|
|
5
|
+
* 1. Rotate existing backups (if target exists).
|
|
6
|
+
* 2. Write to a `.tmp` sibling file.
|
|
7
|
+
* 3. Optionally verify the `.tmp` contents parse correctly.
|
|
8
|
+
* 4. Atomically rename `.tmp` to the target path.
|
|
9
|
+
*
|
|
10
|
+
* On failure during steps 2-4, the `.tmp` file is cleaned up silently.
|
|
11
|
+
*/
|
|
12
|
+
export declare function writeJsonSafe(filePath: string, data: unknown, opts?: WriteOptions): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Attempt to recover a file from its most recent backup.
|
|
15
|
+
*
|
|
16
|
+
* Checks `.bak.1`, `.bak.2`, `.bak.3` in order and copies the first
|
|
17
|
+
* one found back to the target path.
|
|
18
|
+
*
|
|
19
|
+
* @returns `true` if recovery succeeded, `false` if no backup exists.
|
|
20
|
+
*/
|
|
21
|
+
export declare function recoverFromBackup(filePath: string, backupCount?: number): Promise<boolean>;
|
|
22
|
+
//# sourceMappingURL=atomic-writer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-writer.d.ts","sourceRoot":"","sources":["../../../src/engine/safety/atomic-writer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAI1D;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,CAAC,CAsCf;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAuB,GACjC,OAAO,CAAC,OAAO,CAAC,CASlB"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// atomic-writer.ts — Atomic JSON writes with backup rotation (SPEC-094 AC-1)
|
|
2
|
+
import { rename, copyFile, unlink, readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
const DEFAULT_BACKUP_COUNT = 3;
|
|
6
|
+
/**
|
|
7
|
+
* Write JSON data atomically with backup rotation.
|
|
8
|
+
*
|
|
9
|
+
* 1. Rotate existing backups (if target exists).
|
|
10
|
+
* 2. Write to a `.tmp` sibling file.
|
|
11
|
+
* 3. Optionally verify the `.tmp` contents parse correctly.
|
|
12
|
+
* 4. Atomically rename `.tmp` to the target path.
|
|
13
|
+
*
|
|
14
|
+
* On failure during steps 2-4, the `.tmp` file is cleaned up silently.
|
|
15
|
+
*/
|
|
16
|
+
export async function writeJsonSafe(filePath, data, opts) {
|
|
17
|
+
const backupCount = opts?.backupCount ?? DEFAULT_BACKUP_COUNT;
|
|
18
|
+
const verify = opts?.verify ?? true;
|
|
19
|
+
const tmpPath = `${filePath}.tmp`;
|
|
20
|
+
// Ensure parent directory exists
|
|
21
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
22
|
+
// Rotate backups if target already exists
|
|
23
|
+
if (existsSync(filePath)) {
|
|
24
|
+
await rotateBackups(filePath, backupCount);
|
|
25
|
+
await copyFile(filePath, `${filePath}.bak.1`);
|
|
26
|
+
}
|
|
27
|
+
// Serialize
|
|
28
|
+
const json = JSON.stringify(data, null, 2);
|
|
29
|
+
try {
|
|
30
|
+
// Write to temp file
|
|
31
|
+
await writeFile(tmpPath, json, 'utf-8');
|
|
32
|
+
// Verify round-trip if requested
|
|
33
|
+
if (verify) {
|
|
34
|
+
const readBack = await readFile(tmpPath, 'utf-8');
|
|
35
|
+
JSON.parse(readBack);
|
|
36
|
+
}
|
|
37
|
+
// Atomic rename
|
|
38
|
+
await rename(tmpPath, filePath);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
// Cleanup temp file silently
|
|
42
|
+
try {
|
|
43
|
+
await unlink(tmpPath);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Ignore cleanup errors
|
|
47
|
+
}
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Attempt to recover a file from its most recent backup.
|
|
53
|
+
*
|
|
54
|
+
* Checks `.bak.1`, `.bak.2`, `.bak.3` in order and copies the first
|
|
55
|
+
* one found back to the target path.
|
|
56
|
+
*
|
|
57
|
+
* @returns `true` if recovery succeeded, `false` if no backup exists.
|
|
58
|
+
*/
|
|
59
|
+
export async function recoverFromBackup(filePath, backupCount = DEFAULT_BACKUP_COUNT) {
|
|
60
|
+
for (let i = 1; i <= backupCount; i++) {
|
|
61
|
+
const bakPath = `${filePath}.bak.${i}`;
|
|
62
|
+
if (existsSync(bakPath)) {
|
|
63
|
+
await copyFile(bakPath, filePath);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Rotate backup files: `.bak.{count-1}` -> `.bak.{count}`, etc.
|
|
71
|
+
*
|
|
72
|
+
* The oldest backup (`.bak.{count}`) is discarded if it exists.
|
|
73
|
+
*/
|
|
74
|
+
async function rotateBackups(filePath, count) {
|
|
75
|
+
// Delete the oldest if it exists
|
|
76
|
+
const oldestPath = `${filePath}.bak.${count}`;
|
|
77
|
+
if (existsSync(oldestPath)) {
|
|
78
|
+
await unlink(oldestPath);
|
|
79
|
+
}
|
|
80
|
+
// Shift each backup up by one
|
|
81
|
+
for (let i = count - 1; i >= 1; i--) {
|
|
82
|
+
const src = `${filePath}.bak.${i}`;
|
|
83
|
+
const dst = `${filePath}.bak.${i + 1}`;
|
|
84
|
+
if (existsSync(src)) {
|
|
85
|
+
await rename(src, dst);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=atomic-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-writer.js","sourceRoot":"","sources":["../../../src/engine/safety/atomic-writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CircuitState, SafetyCircuitConfig } from '../../types/safety.js';
|
|
2
|
+
export declare class CircuitBreaker {
|
|
3
|
+
private readonly name;
|
|
4
|
+
private state;
|
|
5
|
+
private failureCount;
|
|
6
|
+
private lastFailureTime;
|
|
7
|
+
private readonly config;
|
|
8
|
+
constructor(name: string, config?: Partial<SafetyCircuitConfig>);
|
|
9
|
+
execute<T>(fn: () => Promise<T>, fallback?: T): Promise<T>;
|
|
10
|
+
getState(): CircuitState;
|
|
11
|
+
reset(): void;
|
|
12
|
+
getFailureCount(): number;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=circuit-breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../../src/engine/safety/circuit-breaker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAQ/E,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;gBAE3C,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC;IAKzD,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAqChE,QAAQ,IAAI,YAAY;IAIxB,KAAK,IAAI,IAAI;IAKb,eAAe,IAAI,MAAM;CAG1B"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// SPEC-094 AC-5: Circuit breaker for storage operations
|
|
2
|
+
const DEFAULT_CONFIG = {
|
|
3
|
+
maxFailures: 3,
|
|
4
|
+
resetTimeoutMs: 60_000,
|
|
5
|
+
halfOpenMax: 1,
|
|
6
|
+
};
|
|
7
|
+
export class CircuitBreaker {
|
|
8
|
+
name;
|
|
9
|
+
state = 'closed';
|
|
10
|
+
failureCount = 0;
|
|
11
|
+
lastFailureTime = 0;
|
|
12
|
+
config;
|
|
13
|
+
constructor(name, config) {
|
|
14
|
+
this.name = name;
|
|
15
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
16
|
+
}
|
|
17
|
+
async execute(fn, fallback) {
|
|
18
|
+
if (this.state === 'open') {
|
|
19
|
+
const elapsed = Date.now() - this.lastFailureTime;
|
|
20
|
+
if (elapsed >= this.config.resetTimeoutMs) {
|
|
21
|
+
this.state = 'half-open';
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
if (fallback !== undefined) {
|
|
25
|
+
return fallback;
|
|
26
|
+
}
|
|
27
|
+
throw new Error(`Circuit open for ${this.name}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const wasHalfOpen = this.state === 'half-open';
|
|
31
|
+
try {
|
|
32
|
+
const result = await fn();
|
|
33
|
+
this.state = 'closed';
|
|
34
|
+
this.failureCount = 0;
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
this.failureCount++;
|
|
39
|
+
this.lastFailureTime = Date.now();
|
|
40
|
+
if (wasHalfOpen) {
|
|
41
|
+
this.state = 'open';
|
|
42
|
+
}
|
|
43
|
+
else if (this.failureCount >= this.config.maxFailures) {
|
|
44
|
+
this.state = 'open';
|
|
45
|
+
}
|
|
46
|
+
if (fallback !== undefined) {
|
|
47
|
+
return fallback;
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
getState() {
|
|
53
|
+
return this.state;
|
|
54
|
+
}
|
|
55
|
+
reset() {
|
|
56
|
+
this.state = 'closed';
|
|
57
|
+
this.failureCount = 0;
|
|
58
|
+
}
|
|
59
|
+
getFailureCount() {
|
|
60
|
+
return this.failureCount;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../../src/engine/safety/circuit-breaker.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAIxD,MAAM,cAAc,GAAkC;IACpD,WAAW,EAAE,CAAC;IACd,cAAc,EAAE,MAAM;IACtB,WAAW,EAAE,CAAC;CACf,CAAC;AAEF,MAAM,OAAO,cAAc;IACR,IAAI,CAAS;IACtB,KAAK,GAAiB,QAAQ,CAAC;IAC/B,YAAY,GAAG,CAAC,CAAC;IACjB,eAAe,GAAG,CAAC,CAAC;IACX,MAAM,CAAgC;IAEvD,YAAY,IAAY,EAAE,MAAqC;QAC7D,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,OAAO,CAAI,EAAoB,EAAE,QAAY;QACjD,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC;YAClD,IAAI,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;gBAC1C,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACN,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC3B,OAAO,QAAQ,CAAC;gBAClB,CAAC;gBACD,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC;QAE/C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;YACtB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;YACtB,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAElC,IAAI,WAAW,EAAE,CAAC;gBAChB,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;YACtB,CAAC;iBAAM,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;gBACxD,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;YACtB,CAAC;YAED,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,OAAO,QAAQ,CAAC;YAClB,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;QACtB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LockHandle } from '../../types/safety.js';
|
|
2
|
+
/**
|
|
3
|
+
* Acquires a file-based lock for a named resource.
|
|
4
|
+
* Polls until the lock is available or timeout expires.
|
|
5
|
+
* Stale locks (old timestamp or dead PID) are auto-released.
|
|
6
|
+
*/
|
|
7
|
+
export declare function acquireLock(resource: string, timeoutMs?: number): Promise<LockHandle>;
|
|
8
|
+
/**
|
|
9
|
+
* Releases a previously acquired lock.
|
|
10
|
+
* No-op if the lock file was already removed.
|
|
11
|
+
*/
|
|
12
|
+
export declare function releaseLock(handle: LockHandle): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=file-mutex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-mutex.d.ts","sourceRoot":"","sources":["../../../src/engine/safety/file-mutex.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,uBAAuB,CAAC;AA4CzE;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAA2B,GACrC,OAAO,CAAC,UAAU,CAAC,CAoDrB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAQnE"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// SPEC-094 AC-4: File-based mutex for resource locking
|
|
2
|
+
import { writeFile, readFile, unlink, mkdir } from 'node:fs/promises';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
const LOCK_DIR = 'data/.locks';
|
|
5
|
+
const STALE_THRESHOLD_MS = 30_000;
|
|
6
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
7
|
+
const POLL_INTERVAL_MS = 100;
|
|
8
|
+
function hashResource(resource) {
|
|
9
|
+
return createHash('sha256').update(resource).digest('hex').slice(0, 16);
|
|
10
|
+
}
|
|
11
|
+
function isProcessRunning(pid) {
|
|
12
|
+
try {
|
|
13
|
+
process.kill(pid, 0);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function isStale(content) {
|
|
21
|
+
const lockAge = Date.now() - new Date(content.timestamp).getTime();
|
|
22
|
+
if (lockAge > STALE_THRESHOLD_MS) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (!isProcessRunning(content.pid)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
async function tryReadLock(lockPath) {
|
|
31
|
+
try {
|
|
32
|
+
const raw = await readFile(lockPath, 'utf-8');
|
|
33
|
+
return JSON.parse(raw);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function ensureLockDir() {
|
|
40
|
+
await mkdir(LOCK_DIR, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Acquires a file-based lock for a named resource.
|
|
44
|
+
* Polls until the lock is available or timeout expires.
|
|
45
|
+
* Stale locks (old timestamp or dead PID) are auto-released.
|
|
46
|
+
*/
|
|
47
|
+
export async function acquireLock(resource, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
48
|
+
await ensureLockDir();
|
|
49
|
+
const hash = hashResource(resource);
|
|
50
|
+
const lockPath = `${LOCK_DIR}/${hash}.lock`;
|
|
51
|
+
const deadline = Date.now() + timeoutMs;
|
|
52
|
+
while (Date.now() < deadline) {
|
|
53
|
+
const existing = await tryReadLock(lockPath);
|
|
54
|
+
if (existing) {
|
|
55
|
+
if (isStale(existing)) {
|
|
56
|
+
try {
|
|
57
|
+
await unlink(lockPath);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Another process may have already removed it
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const content = {
|
|
69
|
+
pid: process.pid,
|
|
70
|
+
timestamp: new Date().toISOString(),
|
|
71
|
+
resource,
|
|
72
|
+
};
|
|
73
|
+
try {
|
|
74
|
+
await writeFile(lockPath, JSON.stringify(content, null, 2), {
|
|
75
|
+
flag: 'wx',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// File was created between our check and write — retry
|
|
80
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
resource,
|
|
85
|
+
lockPath,
|
|
86
|
+
pid: process.pid,
|
|
87
|
+
acquiredAt: content.timestamp,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const error = new Error(`Lock timeout after ${timeoutMs}ms for resource "${resource}". Another process holds the lock.`);
|
|
91
|
+
error.code = 'LOCK_TIMEOUT';
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Releases a previously acquired lock.
|
|
96
|
+
* No-op if the lock file was already removed.
|
|
97
|
+
*/
|
|
98
|
+
export async function releaseLock(handle) {
|
|
99
|
+
try {
|
|
100
|
+
await unlink(handle.lockPath);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
if (err.code !== 'ENOENT') {
|
|
104
|
+
throw err;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=file-mutex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-mutex.js","sourceRoot":"","sources":["../../../src/engine/safety/file-mutex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-monitor.d.ts","sourceRoot":"","sources":["../../../src/engine/safety/health-monitor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAYvE,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAiCnF;AAmBD,wBAAgB,kBAAkB,IAAI,YAAY,CAqCjD"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// SPEC-094 AC-8: Health monitoring for storage and service
|
|
2
|
+
import { access, constants, readFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
function aggregateStatus(checks) {
|
|
5
|
+
if (checks.some((c) => c.status === 'fail')) {
|
|
6
|
+
return 'unhealthy';
|
|
7
|
+
}
|
|
8
|
+
if (checks.some((c) => c.status === 'warn')) {
|
|
9
|
+
return 'degraded';
|
|
10
|
+
}
|
|
11
|
+
return 'healthy';
|
|
12
|
+
}
|
|
13
|
+
export async function checkStorageHealth(projectPath) {
|
|
14
|
+
const checks = [];
|
|
15
|
+
// 1. disk-writable
|
|
16
|
+
try {
|
|
17
|
+
await access(projectPath, constants.W_OK);
|
|
18
|
+
checks.push({ name: 'disk-writable', status: 'pass', message: 'Project path is writable' });
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
checks.push({ name: 'disk-writable', status: 'fail', message: 'Project path is not writable' });
|
|
22
|
+
}
|
|
23
|
+
// 2. specs-json
|
|
24
|
+
const specsPath = join(projectPath, 'specs.json');
|
|
25
|
+
checks.push(await checkJsonFile(specsPath, 'specs-json'));
|
|
26
|
+
// 3. decisions-json
|
|
27
|
+
const decisionsPath = join(projectPath, 'decisions.json');
|
|
28
|
+
checks.push(await checkJsonFile(decisionsPath, 'decisions-json'));
|
|
29
|
+
// 4. data-dir-exists
|
|
30
|
+
const dataDir = join(projectPath, '..');
|
|
31
|
+
try {
|
|
32
|
+
await access(dataDir, constants.R_OK);
|
|
33
|
+
checks.push({ name: 'data-dir-exists', status: 'pass', message: 'Data directory exists' });
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
checks.push({ name: 'data-dir-exists', status: 'fail', message: 'Data directory not found' });
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
status: aggregateStatus(checks),
|
|
40
|
+
checks,
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function checkJsonFile(filePath, checkName) {
|
|
45
|
+
try {
|
|
46
|
+
await access(filePath, constants.R_OK);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// File not existing is OK (not yet created)
|
|
50
|
+
return { name: checkName, status: 'pass', message: `${checkName} not found (optional)` };
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const content = await readFile(filePath, 'utf-8');
|
|
54
|
+
JSON.parse(content);
|
|
55
|
+
return { name: checkName, status: 'pass', message: `${checkName} is valid JSON` };
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return { name: checkName, status: 'fail', message: `${checkName} contains corrupt JSON` };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function checkServiceHealth() {
|
|
62
|
+
const checks = [];
|
|
63
|
+
// 1. node-version
|
|
64
|
+
const versionMatch = /^v(\d+)/.exec(process.version);
|
|
65
|
+
const major = versionMatch?.[1] ? parseInt(versionMatch[1], 10) : 0;
|
|
66
|
+
checks.push({
|
|
67
|
+
name: 'node-version',
|
|
68
|
+
status: major >= 18 ? 'pass' : 'warn',
|
|
69
|
+
message: `Node.js ${process.version}`,
|
|
70
|
+
value: process.version,
|
|
71
|
+
});
|
|
72
|
+
// 2. memory-usage
|
|
73
|
+
const heapUsed = process.memoryUsage().heapUsed;
|
|
74
|
+
const heapMB = Math.round(heapUsed / 1024 / 1024);
|
|
75
|
+
checks.push({
|
|
76
|
+
name: 'memory-usage',
|
|
77
|
+
status: heapMB > 500 ? 'warn' : 'pass',
|
|
78
|
+
message: `Heap usage: ${heapMB}MB`,
|
|
79
|
+
value: heapMB,
|
|
80
|
+
});
|
|
81
|
+
// 3. uptime
|
|
82
|
+
const uptimeSeconds = Math.round(process.uptime());
|
|
83
|
+
checks.push({
|
|
84
|
+
name: 'uptime',
|
|
85
|
+
status: 'pass',
|
|
86
|
+
message: `Process uptime: ${uptimeSeconds}s`,
|
|
87
|
+
value: uptimeSeconds,
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
status: aggregateStatus(checks),
|
|
91
|
+
checks,
|
|
92
|
+
timestamp: new Date().toISOString(),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=health-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-monitor.js","sourceRoot":"","sources":["../../../src/engine/safety/health-monitor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { writeJsonSafe, recoverFromBackup } from './atomic-writer.js';
|
|
2
|
+
export { Transaction, SafetyTransactionError } from './transaction.js';
|
|
3
|
+
export { sanitizePath, PathViolationError } from './path-sanitizer.js';
|
|
4
|
+
export { acquireLock, releaseLock } from './file-mutex.js';
|
|
5
|
+
export { CircuitBreaker } from './circuit-breaker.js';
|
|
6
|
+
export { checkStorageHealth, checkServiceHealth } from './health-monitor.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/safety/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// SPEC-094: Safety module barrel export
|
|
2
|
+
export { writeJsonSafe, recoverFromBackup } from './atomic-writer.js';
|
|
3
|
+
export { Transaction, SafetyTransactionError } from './transaction.js';
|
|
4
|
+
export { sanitizePath, PathViolationError } from './path-sanitizer.js';
|
|
5
|
+
export { acquireLock, releaseLock } from './file-mutex.js';
|
|
6
|
+
export { CircuitBreaker } from './circuit-breaker.js';
|
|
7
|
+
export { checkStorageHealth, checkServiceHealth } from './health-monitor.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/safety/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
|