@planu/cli 1.4.0 → 1.5.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 +15 -1
- package/dist/engine/audit-trail/formatter.d.ts +13 -0
- package/dist/engine/audit-trail/formatter.d.ts.map +1 -0
- package/dist/engine/audit-trail/formatter.js +43 -0
- package/dist/engine/audit-trail/formatter.js.map +1 -0
- package/dist/engine/audit-trail/hasher.d.ts +18 -0
- package/dist/engine/audit-trail/hasher.d.ts.map +1 -0
- package/dist/engine/audit-trail/hasher.js +46 -0
- package/dist/engine/audit-trail/hasher.js.map +1 -0
- package/dist/engine/audit-trail/index.d.ts +2 -0
- package/dist/engine/audit-trail/index.d.ts.map +1 -1
- package/dist/engine/audit-trail/index.js +4 -1
- package/dist/engine/audit-trail/index.js.map +1 -1
- package/dist/engine/auto-promoter.d.ts +14 -0
- package/dist/engine/auto-promoter.d.ts.map +1 -0
- package/dist/engine/auto-promoter.js +40 -0
- package/dist/engine/auto-promoter.js.map +1 -0
- package/dist/engine/compliance-test-generator/control-mapper.d.ts +4 -0
- package/dist/engine/compliance-test-generator/control-mapper.d.ts.map +1 -0
- package/dist/engine/compliance-test-generator/control-mapper.js +41 -0
- package/dist/engine/compliance-test-generator/control-mapper.js.map +1 -0
- package/dist/engine/compliance-test-generator/frameworks/iso42001.d.ts +3 -0
- package/dist/engine/compliance-test-generator/frameworks/iso42001.d.ts.map +1 -0
- package/dist/engine/compliance-test-generator/frameworks/iso42001.js +55 -0
- package/dist/engine/compliance-test-generator/frameworks/iso42001.js.map +1 -0
- package/dist/engine/compliance-test-generator/frameworks/pci-dss.d.ts +3 -0
- package/dist/engine/compliance-test-generator/frameworks/pci-dss.d.ts.map +1 -0
- package/dist/engine/compliance-test-generator/frameworks/pci-dss.js +55 -0
- package/dist/engine/compliance-test-generator/frameworks/pci-dss.js.map +1 -0
- package/dist/engine/compliance-test-generator/frameworks/soc2.d.ts +3 -0
- package/dist/engine/compliance-test-generator/frameworks/soc2.d.ts.map +1 -0
- package/dist/engine/compliance-test-generator/frameworks/soc2.js +47 -0
- package/dist/engine/compliance-test-generator/frameworks/soc2.js.map +1 -0
- package/dist/engine/compliance-test-generator/index.d.ts +3 -0
- package/dist/engine/compliance-test-generator/index.d.ts.map +1 -0
- package/dist/engine/compliance-test-generator/index.js +64 -0
- package/dist/engine/compliance-test-generator/index.js.map +1 -0
- package/dist/engine/compliance-test-generator/test-formatter.d.ts +6 -0
- package/dist/engine/compliance-test-generator/test-formatter.d.ts.map +1 -0
- package/dist/engine/compliance-test-generator/test-formatter.js +33 -0
- package/dist/engine/compliance-test-generator/test-formatter.js.map +1 -0
- package/dist/engine/cost-guardrails.d.ts +11 -0
- package/dist/engine/cost-guardrails.d.ts.map +1 -0
- package/dist/engine/cost-guardrails.js +39 -0
- package/dist/engine/cost-guardrails.js.map +1 -0
- package/dist/engine/dogfood-analyzer.d.ts +6 -0
- package/dist/engine/dogfood-analyzer.d.ts.map +1 -0
- package/dist/engine/dogfood-analyzer.js +59 -0
- package/dist/engine/dogfood-analyzer.js.map +1 -0
- package/dist/engine/drift-watcher/criteria-checker.d.ts +16 -0
- package/dist/engine/drift-watcher/criteria-checker.d.ts.map +1 -0
- package/dist/engine/drift-watcher/criteria-checker.js +81 -0
- package/dist/engine/drift-watcher/criteria-checker.js.map +1 -0
- package/dist/engine/drift-watcher/file-watcher.d.ts +8 -0
- package/dist/engine/drift-watcher/file-watcher.d.ts.map +1 -0
- package/dist/engine/drift-watcher/file-watcher.js +24 -0
- package/dist/engine/drift-watcher/file-watcher.js.map +1 -0
- package/dist/engine/drift-watcher/index.d.ts +19 -0
- package/dist/engine/drift-watcher/index.d.ts.map +1 -0
- package/dist/engine/drift-watcher/index.js +70 -0
- package/dist/engine/drift-watcher/index.js.map +1 -0
- package/dist/engine/mcp-gateway/gateway-registry.d.ts +19 -0
- package/dist/engine/mcp-gateway/gateway-registry.d.ts.map +1 -0
- package/dist/engine/mcp-gateway/gateway-registry.js +33 -0
- package/dist/engine/mcp-gateway/gateway-registry.js.map +1 -0
- package/dist/engine/mcp-gateway/gateway-scanner.d.ts +17 -0
- package/dist/engine/mcp-gateway/gateway-scanner.d.ts.map +1 -0
- package/dist/engine/mcp-gateway/gateway-scanner.js +62 -0
- package/dist/engine/mcp-gateway/gateway-scanner.js.map +1 -0
- package/dist/engine/mcp-gateway/index.d.ts +3 -0
- package/dist/engine/mcp-gateway/index.d.ts.map +1 -0
- package/dist/engine/mcp-gateway/index.js +4 -0
- package/dist/engine/mcp-gateway/index.js.map +1 -0
- package/dist/engine/multi-repo/companion-spec-builder.d.ts +9 -0
- package/dist/engine/multi-repo/companion-spec-builder.d.ts.map +1 -0
- package/dist/engine/multi-repo/companion-spec-builder.js +22 -0
- package/dist/engine/multi-repo/companion-spec-builder.js.map +1 -0
- package/dist/engine/multi-repo/consumer-scanner.d.ts +12 -0
- package/dist/engine/multi-repo/consumer-scanner.d.ts.map +1 -0
- package/dist/engine/multi-repo/consumer-scanner.js +48 -0
- package/dist/engine/multi-repo/consumer-scanner.js.map +1 -0
- package/dist/engine/multi-repo/index.d.ts +4 -0
- package/dist/engine/multi-repo/index.d.ts.map +1 -0
- package/dist/engine/multi-repo/index.js +5 -0
- package/dist/engine/multi-repo/index.js.map +1 -0
- package/dist/engine/multi-repo/refactor-tracker.d.ts +7 -0
- package/dist/engine/multi-repo/refactor-tracker.d.ts.map +1 -0
- package/dist/engine/multi-repo/refactor-tracker.js +22 -0
- package/dist/engine/multi-repo/refactor-tracker.js.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/storage/audit-trail-store.d.ts +20 -0
- package/dist/storage/audit-trail-store.d.ts.map +1 -0
- package/dist/storage/audit-trail-store.js +98 -0
- package/dist/storage/audit-trail-store.js.map +1 -0
- package/dist/storage/auto-promoter-config-store.d.ts +11 -0
- package/dist/storage/auto-promoter-config-store.d.ts.map +1 -0
- package/dist/storage/auto-promoter-config-store.js +23 -0
- package/dist/storage/auto-promoter-config-store.js.map +1 -0
- package/dist/storage/budget-store.d.ts +19 -0
- package/dist/storage/budget-store.d.ts.map +1 -0
- package/dist/storage/budget-store.js +64 -0
- package/dist/storage/budget-store.js.map +1 -0
- package/dist/storage/gateway-store.d.ts +23 -0
- package/dist/storage/gateway-store.d.ts.map +1 -0
- package/dist/storage/gateway-store.js +52 -0
- package/dist/storage/gateway-store.js.map +1 -0
- package/dist/storage/refactor-registry-store.d.ts +21 -0
- package/dist/storage/refactor-registry-store.d.ts.map +1 -0
- package/dist/storage/refactor-registry-store.js +77 -0
- package/dist/storage/refactor-registry-store.js.map +1 -0
- package/dist/tools/auto-promoter-handler.d.ts +13 -0
- package/dist/tools/auto-promoter-handler.d.ts.map +1 -0
- package/dist/tools/auto-promoter-handler.js +50 -0
- package/dist/tools/auto-promoter-handler.js.map +1 -0
- package/dist/tools/compliance-test-handler.d.ts +5 -0
- package/dist/tools/compliance-test-handler.d.ts.map +1 -0
- package/dist/tools/compliance-test-handler.js +66 -0
- package/dist/tools/compliance-test-handler.js.map +1 -0
- package/dist/tools/cost-guardrails-handler.d.ts +15 -0
- package/dist/tools/cost-guardrails-handler.d.ts.map +1 -0
- package/dist/tools/cost-guardrails-handler.js +72 -0
- package/dist/tools/cost-guardrails-handler.js.map +1 -0
- package/dist/tools/dogfood-status-handler.d.ts +3 -0
- package/dist/tools/dogfood-status-handler.d.ts.map +1 -0
- package/dist/tools/dogfood-status-handler.js +28 -0
- package/dist/tools/dogfood-status-handler.js.map +1 -0
- package/dist/tools/drift-watcher-handler.d.ts +7 -0
- package/dist/tools/drift-watcher-handler.d.ts.map +1 -0
- package/dist/tools/drift-watcher-handler.js +65 -0
- package/dist/tools/drift-watcher-handler.js.map +1 -0
- package/dist/tools/export-audit-trail-handler.d.ts +9 -0
- package/dist/tools/export-audit-trail-handler.d.ts.map +1 -0
- package/dist/tools/export-audit-trail-handler.js +103 -0
- package/dist/tools/export-audit-trail-handler.js.map +1 -0
- package/dist/tools/mcp-gateway-handler.d.ts +13 -0
- package/dist/tools/mcp-gateway-handler.d.ts.map +1 -0
- package/dist/tools/mcp-gateway-handler.js +60 -0
- package/dist/tools/mcp-gateway-handler.js.map +1 -0
- package/dist/tools/multi-repo-handler.d.ts +15 -0
- package/dist/tools/multi-repo-handler.d.ts.map +1 -0
- package/dist/tools/multi-repo-handler.js +86 -0
- package/dist/tools/multi-repo-handler.js.map +1 -0
- package/dist/tools/register-audit-trail-tools.d.ts.map +1 -1
- package/dist/tools/register-audit-trail-tools.js +38 -0
- package/dist/tools/register-audit-trail-tools.js.map +1 -1
- package/dist/tools/register-auto-promoter-tools.d.ts +3 -0
- package/dist/tools/register-auto-promoter-tools.d.ts.map +1 -0
- package/dist/tools/register-auto-promoter-tools.js +43 -0
- package/dist/tools/register-auto-promoter-tools.js.map +1 -0
- package/dist/tools/register-compliance-test-tools.d.ts +3 -0
- package/dist/tools/register-compliance-test-tools.d.ts.map +1 -0
- package/dist/tools/register-compliance-test-tools.js +55 -0
- package/dist/tools/register-compliance-test-tools.js.map +1 -0
- package/dist/tools/register-cost-guardrails-tools.d.ts +3 -0
- package/dist/tools/register-cost-guardrails-tools.d.ts.map +1 -0
- package/dist/tools/register-cost-guardrails-tools.js +61 -0
- package/dist/tools/register-cost-guardrails-tools.js.map +1 -0
- package/dist/tools/register-dogfood-tools.d.ts +3 -0
- package/dist/tools/register-dogfood-tools.d.ts.map +1 -0
- package/dist/tools/register-dogfood-tools.js +21 -0
- package/dist/tools/register-dogfood-tools.js.map +1 -0
- package/dist/tools/register-drift-watcher-tools.d.ts +3 -0
- package/dist/tools/register-drift-watcher-tools.d.ts.map +1 -0
- package/dist/tools/register-drift-watcher-tools.js +28 -0
- package/dist/tools/register-drift-watcher-tools.js.map +1 -0
- package/dist/tools/register-mcp-gateway-tools.d.ts +3 -0
- package/dist/tools/register-mcp-gateway-tools.d.ts.map +1 -0
- package/dist/tools/register-mcp-gateway-tools.js +35 -0
- package/dist/tools/register-mcp-gateway-tools.js.map +1 -0
- package/dist/tools/register-multi-repo-tools.d.ts +8 -0
- package/dist/tools/register-multi-repo-tools.d.ts.map +1 -0
- package/dist/tools/register-multi-repo-tools.js +52 -0
- package/dist/tools/register-multi-repo-tools.js.map +1 -0
- package/dist/tools/safe-handler.d.ts.map +1 -1
- package/dist/tools/safe-handler.js +50 -1
- package/dist/tools/safe-handler.js.map +1 -1
- package/dist/types/audit.d.ts +64 -0
- package/dist/types/audit.d.ts.map +1 -0
- package/dist/types/audit.js +3 -0
- package/dist/types/audit.js.map +1 -0
- package/dist/types/auto-promoter.d.ts +26 -0
- package/dist/types/auto-promoter.d.ts.map +1 -0
- package/dist/types/auto-promoter.js +3 -0
- package/dist/types/auto-promoter.js.map +1 -0
- package/dist/types/compliance-tests.d.ts +41 -0
- package/dist/types/compliance-tests.d.ts.map +1 -0
- package/dist/types/compliance-tests.js +3 -0
- package/dist/types/compliance-tests.js.map +1 -0
- package/dist/types/cost-guardrails.d.ts +41 -0
- package/dist/types/cost-guardrails.d.ts.map +1 -0
- package/dist/types/cost-guardrails.js +3 -0
- package/dist/types/cost-guardrails.js.map +1 -0
- package/dist/types/dogfood.d.ts +22 -0
- package/dist/types/dogfood.d.ts.map +1 -0
- package/dist/types/dogfood.js +3 -0
- package/dist/types/dogfood.js.map +1 -0
- package/dist/types/drift-watcher.d.ts +35 -0
- package/dist/types/drift-watcher.d.ts.map +1 -0
- package/dist/types/drift-watcher.js +3 -0
- package/dist/types/drift-watcher.js.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/mcp-gateway.d.ts +34 -0
- package/dist/types/mcp-gateway.d.ts.map +1 -0
- package/dist/types/mcp-gateway.js +3 -0
- package/dist/types/mcp-gateway.js.map +1 -0
- package/dist/types/multi-repo.d.ts +39 -0
- package/dist/types/multi-repo.d.ts.map +1 -0
- package/dist/types/multi-repo.js +5 -0
- package/dist/types/multi-repo.js.map +1 -0
- package/package.json +1 -1
- package/src/config/license-plans.json +15 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// engine/compliance-test-generator/test-formatter.ts — SPEC-390: Formats mapped tests to output formats
|
|
2
|
+
export function formatAsJest(test) {
|
|
3
|
+
return (`describe('${test.control.id} — ${test.control.title}', () => {\n` +
|
|
4
|
+
` it('should satisfy: ${test.criterion.slice(0, 80)}', async () => {\n` +
|
|
5
|
+
` // Control: ${test.control.description}\n` +
|
|
6
|
+
` // Rationale: ${test.rationale}\n` +
|
|
7
|
+
` // TODO: implement assertion for this compliance requirement\n` +
|
|
8
|
+
` expect(true).toBe(true); // placeholder\n` +
|
|
9
|
+
` });\n` +
|
|
10
|
+
`});\n`);
|
|
11
|
+
}
|
|
12
|
+
export function formatAsGherkin(test) {
|
|
13
|
+
return (`Feature: ${test.control.id} — ${test.control.title}\n` +
|
|
14
|
+
` Scenario: ${test.criterion.slice(0, 80)}\n` +
|
|
15
|
+
` Given the system is running\n` +
|
|
16
|
+
` When ${test.criterion.slice(0, 60).toLowerCase()}\n` +
|
|
17
|
+
` Then the ${test.control.title.toLowerCase()} control is satisfied\n\n`);
|
|
18
|
+
}
|
|
19
|
+
export function formatAsMarkdown(test) {
|
|
20
|
+
return (`- [ ] **[${test.control.id}]** ${test.criterion}\n` +
|
|
21
|
+
` - Control: ${test.control.title}\n` +
|
|
22
|
+
` - Rationale: ${test.rationale}\n`);
|
|
23
|
+
}
|
|
24
|
+
export function formatTest(test, format) {
|
|
25
|
+
if (format === 'jest') {
|
|
26
|
+
return formatAsJest(test);
|
|
27
|
+
}
|
|
28
|
+
if (format === 'gherkin') {
|
|
29
|
+
return formatAsGherkin(test);
|
|
30
|
+
}
|
|
31
|
+
return formatAsMarkdown(test);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=test-formatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-formatter.js","sourceRoot":"","sources":["../../../src/engine/compliance-test-generator/test-formatter.ts"],"names":[],"mappings":"AAAA,wGAAwG;AAIxG,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,OAAO,CACL,aAAa,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,cAAc;QAClE,yBAAyB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB;QACxE,mBAAmB,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI;QAC/C,qBAAqB,IAAI,CAAC,SAAS,IAAI;QACvC,oEAAoE;QACpE,+CAA+C;QAC/C,SAAS;QACT,OAAO,CACR,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAgB;IAC9C,OAAO,CACL,YAAY,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI;QACvD,eAAe,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI;QAC9C,mCAAmC;QACnC,YAAY,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,IAAI;QACzD,gBAAgB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,2BAA2B,CAC5E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAgB;IAC/C,OAAO,CACL,YAAY,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,IAAI,CAAC,SAAS,IAAI;QACpD,gBAAgB,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI;QACtC,kBAAkB,IAAI,CAAC,SAAS,IAAI,CACrC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAgB,EAAE,MAAwB;IACnE,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SpecBudget, SpecBudgetStatus } from '../types/cost-guardrails.js';
|
|
2
|
+
/**
|
|
3
|
+
* Compute the current budget status for a spec.
|
|
4
|
+
* Recommends a model tier based on usage percentage to control costs.
|
|
5
|
+
*/
|
|
6
|
+
export declare function computeBudgetStatus(budget: SpecBudget): SpecBudgetStatus;
|
|
7
|
+
/**
|
|
8
|
+
* Format a budget status into a human-readable summary message.
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatBudgetMessage(status: SpecBudgetStatus): string;
|
|
11
|
+
//# sourceMappingURL=cost-guardrails.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-guardrails.d.ts","sourceRoot":"","sources":["../../src/engine/cost-guardrails.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAGhF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,CAqBxE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAUpE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// engine/cost-guardrails.ts — Cost Guardrails computation logic (SPEC-393)
|
|
2
|
+
/**
|
|
3
|
+
* Compute the current budget status for a spec.
|
|
4
|
+
* Recommends a model tier based on usage percentage to control costs.
|
|
5
|
+
*/
|
|
6
|
+
export function computeBudgetStatus(budget) {
|
|
7
|
+
const remaining = budget.maxUsdCents - budget.usedUsdCents;
|
|
8
|
+
const usagePercent = budget.maxUsdCents > 0 ? Math.round((budget.usedUsdCents / budget.maxUsdCents) * 100) : 0;
|
|
9
|
+
const warningTriggered = usagePercent >= budget.warningThreshold;
|
|
10
|
+
const exhausted = budget.usedUsdCents >= budget.maxUsdCents;
|
|
11
|
+
// Auto-downgrade model based on remaining budget
|
|
12
|
+
const recommendedModel = exhausted || usagePercent >= 80 ? 'haiku' : usagePercent >= 60 ? 'sonnet' : 'opus';
|
|
13
|
+
return {
|
|
14
|
+
specId: budget.specId,
|
|
15
|
+
maxUsdCents: budget.maxUsdCents,
|
|
16
|
+
usedUsdCents: budget.usedUsdCents,
|
|
17
|
+
remainingUsdCents: Math.max(0, remaining),
|
|
18
|
+
usagePercent,
|
|
19
|
+
warningTriggered,
|
|
20
|
+
recommendedModel,
|
|
21
|
+
exhausted,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Format a budget status into a human-readable summary message.
|
|
26
|
+
*/
|
|
27
|
+
export function formatBudgetMessage(status) {
|
|
28
|
+
const dollars = (status.usedUsdCents / 100).toFixed(2);
|
|
29
|
+
const max = (status.maxUsdCents / 100).toFixed(2);
|
|
30
|
+
let msg = `Budget: $${dollars} / $${max} (${status.usagePercent}%)`;
|
|
31
|
+
if (status.exhausted) {
|
|
32
|
+
msg += ' EXHAUSTED — using haiku';
|
|
33
|
+
}
|
|
34
|
+
else if (status.warningTriggered) {
|
|
35
|
+
msg += ` Warning — downgraded to ${status.recommendedModel}`;
|
|
36
|
+
}
|
|
37
|
+
return msg;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=cost-guardrails.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost-guardrails.js","sourceRoot":"","sources":["../../src/engine/cost-guardrails.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAK3E;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAkB;IACpD,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC;IAC3D,MAAM,YAAY,GAChB,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,gBAAgB,GAAG,YAAY,IAAI,MAAM,CAAC,gBAAgB,CAAC;IACjE,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,WAAW,CAAC;IAE5D,iDAAiD;IACjD,MAAM,gBAAgB,GACpB,SAAS,IAAI,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;IAErF,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,iBAAiB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC;QACzC,YAAY;QACZ,gBAAgB;QAChB,gBAAgB;QAChB,SAAS;KACV,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAwB;IAC1D,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,IAAI,GAAG,GAAG,YAAY,OAAO,OAAO,GAAG,KAAK,MAAM,CAAC,YAAY,IAAI,CAAC;IACpE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,GAAG,IAAI,0BAA0B,CAAC;IACpC,CAAC;SAAM,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;QACnC,GAAG,IAAI,4BAA4B,MAAM,CAAC,gBAAgB,EAAE,CAAC;IAC/D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DogfoodReport } from '../types/dogfood.js';
|
|
2
|
+
export declare const KEY_SDD_TOOLS: string[];
|
|
3
|
+
export declare function getRecentCommitMessages(projectPath: string, sinceDays: number): string[];
|
|
4
|
+
export declare function detectToolUsageFromMessages(messages: string[]): string[];
|
|
5
|
+
export declare function buildDogfoodReport(usedTools: string[], period: string, messages: string[]): DogfoodReport;
|
|
6
|
+
//# sourceMappingURL=dogfood-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dogfood-analyzer.d.ts","sourceRoot":"","sources":["../../src/engine/dogfood-analyzer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzE,eAAO,MAAM,aAAa,UAWzB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAYxF;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CASxE;AAED,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EAAE,EACnB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAAE,GACjB,aAAa,CAuBf"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// dogfood-analyzer.ts — Analyzes git history for Planu tool usage (SPEC-394)
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
export const KEY_SDD_TOOLS = [
|
|
4
|
+
'create_spec',
|
|
5
|
+
'validate',
|
|
6
|
+
'challenge_spec',
|
|
7
|
+
'check_readiness',
|
|
8
|
+
'update_status',
|
|
9
|
+
'estimate',
|
|
10
|
+
'suggest_criteria',
|
|
11
|
+
'inject_criteria',
|
|
12
|
+
'doc_compliance_report',
|
|
13
|
+
'generate_skill',
|
|
14
|
+
];
|
|
15
|
+
export function getRecentCommitMessages(projectPath, sinceDays) {
|
|
16
|
+
try {
|
|
17
|
+
const since = new Date(Date.now() - sinceDays * 86400000).toISOString().split('T')[0];
|
|
18
|
+
const output = execSync(`git log --since="${since}" --pretty=format:"%s %b" --no-merges`, {
|
|
19
|
+
cwd: projectPath,
|
|
20
|
+
encoding: 'utf-8',
|
|
21
|
+
timeout: 5000,
|
|
22
|
+
});
|
|
23
|
+
return output.split('\n').filter(Boolean);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function detectToolUsageFromMessages(messages) {
|
|
30
|
+
const used = new Set();
|
|
31
|
+
const combined = messages.join(' ').toLowerCase();
|
|
32
|
+
for (const tool of KEY_SDD_TOOLS) {
|
|
33
|
+
if (combined.includes(tool.replace(/_/g, '_')) || combined.includes(tool.replace(/_/g, '-'))) {
|
|
34
|
+
used.add(tool);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return [...used];
|
|
38
|
+
}
|
|
39
|
+
export function buildDogfoodReport(usedTools, period, messages) {
|
|
40
|
+
const missingTools = KEY_SDD_TOOLS.filter((t) => !usedTools.includes(t));
|
|
41
|
+
const dogfoodScore = Math.round((usedTools.length / KEY_SDD_TOOLS.length) * 100);
|
|
42
|
+
const combined = messages.join(' ').toLowerCase();
|
|
43
|
+
const metrics = {
|
|
44
|
+
specCreated: (combined.match(/create_spec|feat.*spec-\d+/g) ?? []).length,
|
|
45
|
+
specValidated: (combined.match(/validate|validation/g) ?? []).length,
|
|
46
|
+
specChallenged: (combined.match(/challenge_spec|challenge/g) ?? []).length,
|
|
47
|
+
readinessChecked: (combined.match(/check_readiness|readiness/g) ?? []).length,
|
|
48
|
+
statusUpdated: (combined.match(/update_status|status.*done|status.*impl/g) ?? []).length,
|
|
49
|
+
totalToolUses: usedTools.length,
|
|
50
|
+
coveragePercent: dogfoodScore,
|
|
51
|
+
};
|
|
52
|
+
const recommendation = dogfoodScore >= 80
|
|
53
|
+
? 'Excellent dogfooding! Keep using Planu throughout development.'
|
|
54
|
+
: dogfoodScore >= 50
|
|
55
|
+
? `Good progress. Missing: ${missingTools.slice(0, 3).join(', ')}`
|
|
56
|
+
: `Improve SDD adoption. Start with: ${missingTools.slice(0, 3).join(', ')}`;
|
|
57
|
+
return { period, metrics, usedTools, missingTools, recommendation, dogfoodScore };
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=dogfood-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dogfood-analyzer.js","sourceRoot":"","sources":["../../src/engine/dogfood-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DriftAlert } from '../../types/drift-watcher.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parse spec.md lines starting with "- [ ]" or "- [x]" as criteria.
|
|
4
|
+
* Returns the criterion text stripped of the checkbox prefix.
|
|
5
|
+
*/
|
|
6
|
+
export declare function extractCriteriaFromSpec(specPath: string): Promise<string[]>;
|
|
7
|
+
/**
|
|
8
|
+
* Check if a file's content contains key terms from the criterion.
|
|
9
|
+
* Returns true if any key term is found in the file.
|
|
10
|
+
*/
|
|
11
|
+
export declare function checkCriterionInFile(criterion: string, filePath: string): Promise<boolean>;
|
|
12
|
+
/**
|
|
13
|
+
* Build a DriftAlert for a criterion that may no longer be satisfied.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildDriftAlert(specId: string, criterion: string, filePath: string): DriftAlert;
|
|
16
|
+
//# sourceMappingURL=criteria-checker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"criteria-checker.d.ts","sourceRoot":"","sources":["../../../src/engine/drift-watcher/criteria-checker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAK/D;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkBjF;AAYD;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAShG;AAiBD;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAU/F"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// engine/drift-watcher/criteria-checker.ts — SPEC-388: criterion checks on file change
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
const HIGH_SEVERITY_KEYWORDS = ['auth', 'security', 'permission', 'access', 'token', 'password'];
|
|
4
|
+
const MEDIUM_SEVERITY_KEYWORDS = ['validate', 'error', 'fail', 'reject', 'limit', 'timeout'];
|
|
5
|
+
/**
|
|
6
|
+
* Parse spec.md lines starting with "- [ ]" or "- [x]" as criteria.
|
|
7
|
+
* Returns the criterion text stripped of the checkbox prefix.
|
|
8
|
+
*/
|
|
9
|
+
export async function extractCriteriaFromSpec(specPath) {
|
|
10
|
+
try {
|
|
11
|
+
const content = await readFile(specPath, 'utf-8');
|
|
12
|
+
const lines = content.split('\n');
|
|
13
|
+
const criteria = [];
|
|
14
|
+
for (const line of lines) {
|
|
15
|
+
const trimmed = line.trim();
|
|
16
|
+
if (trimmed.startsWith('- [ ]') || trimmed.startsWith('- [x]')) {
|
|
17
|
+
const text = trimmed.replace(/^- \[[ x]\]\s*/, '').trim();
|
|
18
|
+
if (text.length > 0) {
|
|
19
|
+
criteria.push(text);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return criteria;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Extract key terms from a criterion (words >=4 chars, lowercased).
|
|
31
|
+
*/
|
|
32
|
+
function extractKeyTerms(criterion) {
|
|
33
|
+
return criterion
|
|
34
|
+
.toLowerCase()
|
|
35
|
+
.split(/\W+/)
|
|
36
|
+
.filter((w) => w.length >= 4);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if a file's content contains key terms from the criterion.
|
|
40
|
+
* Returns true if any key term is found in the file.
|
|
41
|
+
*/
|
|
42
|
+
export async function checkCriterionInFile(criterion, filePath) {
|
|
43
|
+
try {
|
|
44
|
+
const content = await readFile(filePath, 'utf-8');
|
|
45
|
+
const lower = content.toLowerCase();
|
|
46
|
+
const terms = extractKeyTerms(criterion);
|
|
47
|
+
return terms.some((term) => lower.includes(term));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Determine severity based on criterion keywords.
|
|
55
|
+
* auth/security keywords = high, validate/error = medium, otherwise low.
|
|
56
|
+
*/
|
|
57
|
+
function determineSeverity(criterion) {
|
|
58
|
+
const lower = criterion.toLowerCase();
|
|
59
|
+
if (HIGH_SEVERITY_KEYWORDS.some((kw) => lower.includes(kw))) {
|
|
60
|
+
return 'high';
|
|
61
|
+
}
|
|
62
|
+
if (MEDIUM_SEVERITY_KEYWORDS.some((kw) => lower.includes(kw))) {
|
|
63
|
+
return 'medium';
|
|
64
|
+
}
|
|
65
|
+
return 'low';
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build a DriftAlert for a criterion that may no longer be satisfied.
|
|
69
|
+
*/
|
|
70
|
+
export function buildDriftAlert(specId, criterion, filePath) {
|
|
71
|
+
const severity = determineSeverity(criterion);
|
|
72
|
+
return {
|
|
73
|
+
specId,
|
|
74
|
+
criterionText: criterion,
|
|
75
|
+
affectedFile: filePath,
|
|
76
|
+
detectedAt: new Date().toISOString(),
|
|
77
|
+
severity,
|
|
78
|
+
suggestedFix: `Review ${filePath} to ensure it still satisfies: "${criterion}"`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=criteria-checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"criteria-checker.js","sourceRoot":"","sources":["../../../src/engine/drift-watcher/criteria-checker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watch a directory recursively for file changes.
|
|
3
|
+
* Calls onChange with the relative filename whenever a file changes.
|
|
4
|
+
* Silently skips directories that don't exist or can't be watched.
|
|
5
|
+
* Closes the watcher when the provided AbortSignal is aborted.
|
|
6
|
+
*/
|
|
7
|
+
export declare function watchDirectory(dirPath: string, onChange: (filename: string) => void, signal: AbortSignal): void;
|
|
8
|
+
//# sourceMappingURL=file-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../../src/engine/drift-watcher/file-watcher.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACpC,MAAM,EAAE,WAAW,GAClB,IAAI,CAiBN"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// engine/drift-watcher/file-watcher.ts — SPEC-388: FSEvents wrapper using node:fs watch
|
|
2
|
+
import { watch } from 'node:fs';
|
|
3
|
+
/**
|
|
4
|
+
* Watch a directory recursively for file changes.
|
|
5
|
+
* Calls onChange with the relative filename whenever a file changes.
|
|
6
|
+
* Silently skips directories that don't exist or can't be watched.
|
|
7
|
+
* Closes the watcher when the provided AbortSignal is aborted.
|
|
8
|
+
*/
|
|
9
|
+
export function watchDirectory(dirPath, onChange, signal) {
|
|
10
|
+
try {
|
|
11
|
+
const watcher = watch(dirPath, { recursive: true }, (_event, filename) => {
|
|
12
|
+
if (filename) {
|
|
13
|
+
onChange(filename);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
signal.addEventListener('abort', () => {
|
|
17
|
+
watcher.close();
|
|
18
|
+
}, { once: true });
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Directory doesn't exist or not watchable — silently skip
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=file-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-watcher.js","sourceRoot":"","sources":["../../../src/engine/drift-watcher/file-watcher.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,QAAoC,EACpC,MAAmB;IAEnB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE;YACvE,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,gBAAgB,CACrB,OAAO,EACP,GAAG,EAAE;YACH,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC,EACD,EAAE,IAAI,EAAE,IAAI,EAAE,CACf,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,2DAA2D;IAC7D,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DriftWatcherConfig, WatcherState } from '../../types/drift-watcher.js';
|
|
2
|
+
/**
|
|
3
|
+
* Start watching a spec's associated paths for drift.
|
|
4
|
+
* Returns the initial WatcherState.
|
|
5
|
+
*/
|
|
6
|
+
export declare function startWatcher(config: DriftWatcherConfig): WatcherState;
|
|
7
|
+
/**
|
|
8
|
+
* Stop a running watcher. Returns true if it was running, false if not found.
|
|
9
|
+
*/
|
|
10
|
+
export declare function stopWatcher(specId: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Get the current state of a watcher. Returns null if not running.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getWatcherState(specId: string): WatcherState | null;
|
|
15
|
+
/**
|
|
16
|
+
* List all active watcher states.
|
|
17
|
+
*/
|
|
18
|
+
export declare function listActiveWatchers(): WatcherState[];
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/drift-watcher/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAgB,MAAM,8BAA8B,CAAC;AAuBnG;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CA8BrE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAUnD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAEnE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,EAAE,CAEnD"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { watchDirectory } from './file-watcher.js';
|
|
2
|
+
import { extractCriteriaFromSpec, buildDriftAlert } from './criteria-checker.js';
|
|
3
|
+
// In-memory registry — does not persist across server restarts
|
|
4
|
+
const activeWatchers = new Map();
|
|
5
|
+
async function runCriteriaCheck(config, filename) {
|
|
6
|
+
const entry = activeWatchers.get(config.specId);
|
|
7
|
+
if (!entry) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const filePath = filename;
|
|
11
|
+
const criteria = await extractCriteriaFromSpec(config.specPath);
|
|
12
|
+
for (const criterion of criteria) {
|
|
13
|
+
const alert = buildDriftAlert(config.specId, criterion, filePath);
|
|
14
|
+
entry.state.alertCount++;
|
|
15
|
+
entry.state.lastAlert = alert;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Start watching a spec's associated paths for drift.
|
|
20
|
+
* Returns the initial WatcherState.
|
|
21
|
+
*/
|
|
22
|
+
export function startWatcher(config) {
|
|
23
|
+
const existing = activeWatchers.get(config.specId);
|
|
24
|
+
if (existing) {
|
|
25
|
+
return existing.state;
|
|
26
|
+
}
|
|
27
|
+
const controller = new AbortController();
|
|
28
|
+
const state = {
|
|
29
|
+
specId: config.specId,
|
|
30
|
+
projectPath: config.projectPath,
|
|
31
|
+
active: true,
|
|
32
|
+
startedAt: new Date().toISOString(),
|
|
33
|
+
alertCount: 0,
|
|
34
|
+
};
|
|
35
|
+
activeWatchers.set(config.specId, { state, controller });
|
|
36
|
+
for (const watchedPath of config.watchedPaths) {
|
|
37
|
+
watchDirectory(watchedPath, (filename) => {
|
|
38
|
+
runCriteriaCheck(config, filename).catch(() => {
|
|
39
|
+
// Silently ignore errors during criteria check
|
|
40
|
+
});
|
|
41
|
+
}, controller.signal);
|
|
42
|
+
}
|
|
43
|
+
return state;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Stop a running watcher. Returns true if it was running, false if not found.
|
|
47
|
+
*/
|
|
48
|
+
export function stopWatcher(specId) {
|
|
49
|
+
const entry = activeWatchers.get(specId);
|
|
50
|
+
if (!entry) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
entry.controller.abort();
|
|
54
|
+
entry.state.active = false;
|
|
55
|
+
activeWatchers.delete(specId);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the current state of a watcher. Returns null if not running.
|
|
60
|
+
*/
|
|
61
|
+
export function getWatcherState(specId) {
|
|
62
|
+
return activeWatchers.get(specId)?.state ?? null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* List all active watcher states.
|
|
66
|
+
*/
|
|
67
|
+
export function listActiveWatchers() {
|
|
68
|
+
return [...activeWatchers.values()].map((e) => e.state);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/drift-watcher/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEjF,+DAA+D;AAC/D,MAAM,cAAc,GAAG,IAAI,GAAG,EAAwB,CAAC;AAEvD,KAAK,UAAU,gBAAgB,CAAC,MAA0B,EAAE,QAAgB;IAC1E,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAC1B,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAEhE,KAAK,MAAM,SAAS,IAAI,QAAQ,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QAClE,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACzB,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;IAChC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAA0B;IACrD,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC,KAAK,CAAC;IACxB,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAiB;QAC1B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,MAAM,EAAE,IAAI;QACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,UAAU,EAAE,CAAC;KACd,CAAC;IAEF,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IAEzD,KAAK,MAAM,WAAW,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QAC9C,cAAc,CACZ,WAAW,EACX,CAAC,QAAQ,EAAE,EAAE;YACX,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC5C,+CAA+C;YACjD,CAAC,CAAC,CAAC;QACL,CAAC,EACD,UAAU,CAAC,MAAM,CAClB,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACzB,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;IAC3B,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9B,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,OAAO,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;AACnD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DiscoveredGateway, GatewayRegistry } from '../../types/mcp-gateway.js';
|
|
2
|
+
/**
|
|
3
|
+
* Run a full discovery pass:
|
|
4
|
+
* 1. Optionally scan default localhost ports.
|
|
5
|
+
* 2. Probe any explicitly provided URLs.
|
|
6
|
+
* 3. Persist every found gateway via mergeGateway (upsert).
|
|
7
|
+
*
|
|
8
|
+
* Returns all gateways found in this pass (may be empty).
|
|
9
|
+
*/
|
|
10
|
+
export declare function discoverGateways(urls: string[], includeLocalhost: boolean): Promise<DiscoveredGateway[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Load the full gateway registry and return it along with the active subset.
|
|
13
|
+
* Encapsulates storage access so tool handlers don't import storage directly.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getGatewayStatus(): Promise<{
|
|
16
|
+
registry: GatewayRegistry;
|
|
17
|
+
active: DiscoveredGateway[];
|
|
18
|
+
}>;
|
|
19
|
+
//# sourceMappingURL=gateway-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-registry.d.ts","sourceRoot":"","sources":["../../../src/engine/mcp-gateway/gateway-registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAIrF;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EAAE,EACd,gBAAgB,EAAE,OAAO,GACxB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAgB9B;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC;IAChD,QAAQ,EAAE,eAAe,CAAC;IAC1B,MAAM,EAAE,iBAAiB,EAAE,CAAC;CAC7B,CAAC,CAID"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { scanLocalhostPorts, scanUrls } from './gateway-scanner.js';
|
|
2
|
+
import { mergeGateway, loadRegistry, listActive } from '../../storage/gateway-store.js';
|
|
3
|
+
/**
|
|
4
|
+
* Run a full discovery pass:
|
|
5
|
+
* 1. Optionally scan default localhost ports.
|
|
6
|
+
* 2. Probe any explicitly provided URLs.
|
|
7
|
+
* 3. Persist every found gateway via mergeGateway (upsert).
|
|
8
|
+
*
|
|
9
|
+
* Returns all gateways found in this pass (may be empty).
|
|
10
|
+
*/
|
|
11
|
+
export async function discoverGateways(urls, includeLocalhost) {
|
|
12
|
+
const found = [];
|
|
13
|
+
if (includeLocalhost) {
|
|
14
|
+
found.push(...(await scanLocalhostPorts()));
|
|
15
|
+
}
|
|
16
|
+
if (urls.length > 0) {
|
|
17
|
+
found.push(...(await scanUrls(urls)));
|
|
18
|
+
}
|
|
19
|
+
for (const gw of found) {
|
|
20
|
+
await mergeGateway(gw);
|
|
21
|
+
}
|
|
22
|
+
return found;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Load the full gateway registry and return it along with the active subset.
|
|
26
|
+
* Encapsulates storage access so tool handlers don't import storage directly.
|
|
27
|
+
*/
|
|
28
|
+
export async function getGatewayStatus() {
|
|
29
|
+
const registry = await loadRegistry();
|
|
30
|
+
const active = listActive(registry);
|
|
31
|
+
return { registry, active };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=gateway-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-registry.js","sourceRoot":"","sources":["../../../src/engine/mcp-gateway/gateway-registry.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAExF;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAc,EACd,gBAAyB;IAEzB,MAAM,KAAK,GAAwB,EAAE,CAAC;IAEtC,IAAI,gBAAgB,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,kBAAkB,EAAE,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,YAAY,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IAIpC,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DiscoveredGateway } from '../../types/mcp-gateway.js';
|
|
2
|
+
/**
|
|
3
|
+
* Probe a single base URL for a /.well-known/mcp manifest.
|
|
4
|
+
* Returns null when the endpoint is absent, non-OK, or times out.
|
|
5
|
+
*/
|
|
6
|
+
export declare function probeGatewayUrl(baseUrl: string): Promise<DiscoveredGateway | null>;
|
|
7
|
+
/**
|
|
8
|
+
* Scan all default localhost ports in parallel.
|
|
9
|
+
* Returns only the gateways that responded successfully.
|
|
10
|
+
*/
|
|
11
|
+
export declare function scanLocalhostPorts(): Promise<DiscoveredGateway[]>;
|
|
12
|
+
/**
|
|
13
|
+
* Scan a list of explicit URLs in parallel.
|
|
14
|
+
* Returns only the gateways that responded successfully.
|
|
15
|
+
*/
|
|
16
|
+
export declare function scanUrls(urls: string[]): Promise<DiscoveredGateway[]>;
|
|
17
|
+
//# sourceMappingURL=gateway-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-scanner.d.ts","sourceRoot":"","sources":["../../../src/engine/mcp-gateway/gateway-scanner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAsB,MAAM,4BAA4B,CAAC;AAQxF;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAkCxF;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAUvE;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAQ3E"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const WELL_KNOWN_PATH = '/.well-known/mcp';
|
|
2
|
+
const DEFAULT_TIMEOUT_MS = 3000;
|
|
3
|
+
/** Default localhost ports to scan for MCP servers. */
|
|
4
|
+
const DEFAULT_LOCAL_PORTS = [3000, 3001, 8080, 8000, 9000];
|
|
5
|
+
/**
|
|
6
|
+
* Probe a single base URL for a /.well-known/mcp manifest.
|
|
7
|
+
* Returns null when the endpoint is absent, non-OK, or times out.
|
|
8
|
+
*/
|
|
9
|
+
export async function probeGatewayUrl(baseUrl) {
|
|
10
|
+
try {
|
|
11
|
+
const url = `${baseUrl.replace(/\/$/, '')}${WELL_KNOWN_PATH}`;
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
const timer = setTimeout(() => {
|
|
14
|
+
controller.abort();
|
|
15
|
+
}, DEFAULT_TIMEOUT_MS);
|
|
16
|
+
let res;
|
|
17
|
+
try {
|
|
18
|
+
res = await fetch(url, { signal: controller.signal });
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
}
|
|
23
|
+
if (!res.ok) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const manifest = (await res.json());
|
|
27
|
+
const now = new Date().toISOString();
|
|
28
|
+
return {
|
|
29
|
+
url: baseUrl,
|
|
30
|
+
name: manifest.name ?? baseUrl,
|
|
31
|
+
discoveredAt: now,
|
|
32
|
+
lastSeenAt: now,
|
|
33
|
+
planuCompatible: manifest.planuCompatible ?? manifest.tools?.includes('create_spec') ?? false,
|
|
34
|
+
tools: manifest.tools ?? [],
|
|
35
|
+
status: 'active',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Scan all default localhost ports in parallel.
|
|
44
|
+
* Returns only the gateways that responded successfully.
|
|
45
|
+
*/
|
|
46
|
+
export async function scanLocalhostPorts() {
|
|
47
|
+
const results = await Promise.allSettled(DEFAULT_LOCAL_PORTS.map((port) => probeGatewayUrl(`http://localhost:${port}`)));
|
|
48
|
+
return results
|
|
49
|
+
.filter((r) => r.status === 'fulfilled' && r.value !== null)
|
|
50
|
+
.map((r) => r.value);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Scan a list of explicit URLs in parallel.
|
|
54
|
+
* Returns only the gateways that responded successfully.
|
|
55
|
+
*/
|
|
56
|
+
export async function scanUrls(urls) {
|
|
57
|
+
const results = await Promise.allSettled(urls.map(probeGatewayUrl));
|
|
58
|
+
return results
|
|
59
|
+
.filter((r) => r.status === 'fulfilled' && r.value !== null)
|
|
60
|
+
.map((r) => r.value);
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=gateway-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-scanner.js","sourceRoot":"","sources":["../../../src/engine/mcp-gateway/gateway-scanner.ts"],"names":[],"mappings":"AAGA,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAC3C,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC,uDAAuD;AACvD,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAE3D;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe;IACnD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,eAAe,EAAE,CAAC;QAC9D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC,EAAE,kBAAkB,CAAC,CAAC;QAEvB,IAAI,GAAa,CAAC;QAClB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;QACxD,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAC;QAC1D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAErC,OAAO;YACL,GAAG,EAAE,OAAO;YACZ,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,OAAO;YAC9B,YAAY,EAAE,GAAG;YACjB,UAAU,EAAE,GAAG;YACf,eAAe,EAAE,QAAQ,CAAC,eAAe,IAAI,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,IAAI,KAAK;YAC7F,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,EAAE;YAC3B,MAAM,EAAE,QAAQ;SACjB,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACtC,mBAAmB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,oBAAoB,IAAI,EAAE,CAAC,CAAC,CAC/E,CAAC;IACF,OAAO,OAAO;SACX,MAAM,CACL,CAAC,CAAC,EAAkD,EAAE,CACpD,CAAC,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,CAC/C;SACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAc;IAC3C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;IACpE,OAAO,OAAO;SACX,MAAM,CACL,CAAC,CAAC,EAAkD,EAAE,CACpD,CAAC,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,CAC/C;SACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/mcp-gateway/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/mcp-gateway/index.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a human-readable description for a companion spec created in a consumer project.
|
|
3
|
+
*/
|
|
4
|
+
export declare function buildCompanionSpecDescription(sourceSpecId: string, sourceProjectPath: string, _consumerProjectPath: string, affectedSymbols: string[], usageLocations: string[]): string;
|
|
5
|
+
/**
|
|
6
|
+
* Builds a concise title for a companion spec referencing the source spec.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildCompanionSpecTitle(sourceSpecId: string, symbols: string[]): string;
|
|
9
|
+
//# sourceMappingURL=companion-spec-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"companion-spec-builder.d.ts","sourceRoot":"","sources":["../../../src/engine/multi-repo/companion-spec-builder.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,MAAM,EACzB,oBAAoB,EAAE,MAAM,EAC5B,eAAe,EAAE,MAAM,EAAE,EACzB,cAAc,EAAE,MAAM,EAAE,GACvB,MAAM,CASR;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAGvF"}
|