thevoidforge 21.0.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/scripts/vault-read.d.ts +11 -0
- package/dist/scripts/vault-read.js +89 -0
- package/dist/scripts/voidforge.d.ts +20 -0
- package/dist/scripts/voidforge.js +404 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/wizard/api/auth.d.ts +5 -0
- package/dist/wizard/api/auth.js +133 -0
- package/dist/wizard/api/blueprint.d.ts +45 -0
- package/dist/wizard/api/blueprint.js +184 -0
- package/dist/wizard/api/cloud-providers.d.ts +16 -0
- package/dist/wizard/api/cloud-providers.js +363 -0
- package/dist/wizard/api/credentials.d.ts +1 -0
- package/dist/wizard/api/credentials.js +258 -0
- package/dist/wizard/api/danger-room.d.ts +18 -0
- package/dist/wizard/api/danger-room.js +401 -0
- package/dist/wizard/api/deploy.d.ts +4 -0
- package/dist/wizard/api/deploy.js +164 -0
- package/dist/wizard/api/prd.d.ts +1 -0
- package/dist/wizard/api/prd.js +363 -0
- package/dist/wizard/api/project.d.ts +1 -0
- package/dist/wizard/api/project.js +239 -0
- package/dist/wizard/api/projects.d.ts +6 -0
- package/dist/wizard/api/projects.js +648 -0
- package/dist/wizard/api/provision.d.ts +4 -0
- package/dist/wizard/api/provision.js +535 -0
- package/dist/wizard/api/terminal.d.ts +25 -0
- package/dist/wizard/api/terminal.js +241 -0
- package/dist/wizard/api/users.d.ts +6 -0
- package/dist/wizard/api/users.js +244 -0
- package/dist/wizard/api/war-room.d.ts +14 -0
- package/dist/wizard/api/war-room.js +45 -0
- package/dist/wizard/lib/ad-platform-core.d.ts +6 -0
- package/dist/wizard/lib/ad-platform-core.js +1 -0
- package/dist/wizard/lib/adapters/index.d.ts +52 -0
- package/dist/wizard/lib/adapters/index.js +38 -0
- package/dist/wizard/lib/adapters/sandbox-bank.d.ts +17 -0
- package/dist/wizard/lib/adapters/sandbox-bank.js +77 -0
- package/dist/wizard/lib/adapters/sandbox.d.ts +39 -0
- package/dist/wizard/lib/adapters/sandbox.js +174 -0
- package/dist/wizard/lib/adapters/stripe.d.ts +19 -0
- package/dist/wizard/lib/adapters/stripe.js +143 -0
- package/dist/wizard/lib/adapters/types.d.ts +9 -0
- package/dist/wizard/lib/adapters/types.js +10 -0
- package/dist/wizard/lib/agent-memory.d.ts +36 -0
- package/dist/wizard/lib/agent-memory.js +114 -0
- package/dist/wizard/lib/anomaly-detection.d.ts +59 -0
- package/dist/wizard/lib/anomaly-detection.js +122 -0
- package/dist/wizard/lib/anthropic.d.ts +21 -0
- package/dist/wizard/lib/anthropic.js +105 -0
- package/dist/wizard/lib/asset-scanner.d.ts +23 -0
- package/dist/wizard/lib/asset-scanner.js +107 -0
- package/dist/wizard/lib/audit-log.d.ts +23 -0
- package/dist/wizard/lib/audit-log.js +70 -0
- package/dist/wizard/lib/autonomy-controller.d.ts +76 -0
- package/dist/wizard/lib/autonomy-controller.js +183 -0
- package/dist/wizard/lib/body-parser.d.ts +2 -0
- package/dist/wizard/lib/body-parser.js +36 -0
- package/dist/wizard/lib/build-analytics.d.ts +39 -0
- package/dist/wizard/lib/build-analytics.js +91 -0
- package/dist/wizard/lib/build-step.d.ts +21 -0
- package/dist/wizard/lib/build-step.js +104 -0
- package/dist/wizard/lib/campaign-proposer.d.ts +39 -0
- package/dist/wizard/lib/campaign-proposer.js +180 -0
- package/dist/wizard/lib/campaign-state-machine.d.ts +63 -0
- package/dist/wizard/lib/campaign-state-machine.js +114 -0
- package/dist/wizard/lib/ci-generator.d.ts +14 -0
- package/dist/wizard/lib/ci-generator.js +187 -0
- package/dist/wizard/lib/claude-merge.d.ts +38 -0
- package/dist/wizard/lib/claude-merge.js +115 -0
- package/dist/wizard/lib/codegen/erd-gen.d.ts +16 -0
- package/dist/wizard/lib/codegen/erd-gen.js +98 -0
- package/dist/wizard/lib/codegen/integrations.d.ts +18 -0
- package/dist/wizard/lib/codegen/integrations.js +189 -0
- package/dist/wizard/lib/codegen/openapi-gen.d.ts +15 -0
- package/dist/wizard/lib/codegen/openapi-gen.js +79 -0
- package/dist/wizard/lib/codegen/prisma-types.d.ts +15 -0
- package/dist/wizard/lib/codegen/prisma-types.js +44 -0
- package/dist/wizard/lib/codegen/seed-gen.d.ts +16 -0
- package/dist/wizard/lib/codegen/seed-gen.js +128 -0
- package/dist/wizard/lib/compliance.d.ts +51 -0
- package/dist/wizard/lib/compliance.js +112 -0
- package/dist/wizard/lib/correlation-engine.d.ts +59 -0
- package/dist/wizard/lib/correlation-engine.js +151 -0
- package/dist/wizard/lib/cost-estimator.d.ts +22 -0
- package/dist/wizard/lib/cost-estimator.js +72 -0
- package/dist/wizard/lib/cost-tracker.d.ts +27 -0
- package/dist/wizard/lib/cost-tracker.js +37 -0
- package/dist/wizard/lib/daemon-aggregator.d.ts +71 -0
- package/dist/wizard/lib/daemon-aggregator.js +204 -0
- package/dist/wizard/lib/daemon-core.d.ts +6 -0
- package/dist/wizard/lib/daemon-core.js +5 -0
- package/dist/wizard/lib/dashboard-data.d.ts +132 -0
- package/dist/wizard/lib/dashboard-data.js +336 -0
- package/dist/wizard/lib/dashboard-ws.d.ts +25 -0
- package/dist/wizard/lib/dashboard-ws.js +91 -0
- package/dist/wizard/lib/deep-current.d.ts +77 -0
- package/dist/wizard/lib/deep-current.js +234 -0
- package/dist/wizard/lib/deploy-coordinator.d.ts +40 -0
- package/dist/wizard/lib/deploy-coordinator.js +86 -0
- package/dist/wizard/lib/deploy-log.d.ts +28 -0
- package/dist/wizard/lib/deploy-log.js +52 -0
- package/dist/wizard/lib/desktop-notify.d.ts +27 -0
- package/dist/wizard/lib/desktop-notify.js +98 -0
- package/dist/wizard/lib/dns/cloudflare-dns.d.ts +35 -0
- package/dist/wizard/lib/dns/cloudflare-dns.js +216 -0
- package/dist/wizard/lib/dns/cloudflare-registrar.d.ts +31 -0
- package/dist/wizard/lib/dns/cloudflare-registrar.js +148 -0
- package/dist/wizard/lib/dns/types.d.ts +22 -0
- package/dist/wizard/lib/dns/types.js +4 -0
- package/dist/wizard/lib/document-discovery.d.ts +33 -0
- package/dist/wizard/lib/document-discovery.js +145 -0
- package/dist/wizard/lib/env-validator.d.ts +14 -0
- package/dist/wizard/lib/env-validator.js +205 -0
- package/dist/wizard/lib/env-writer.d.ts +13 -0
- package/dist/wizard/lib/env-writer.js +26 -0
- package/dist/wizard/lib/exec.d.ts +30 -0
- package/dist/wizard/lib/exec.js +52 -0
- package/dist/wizard/lib/experiment.d.ts +70 -0
- package/dist/wizard/lib/experiment.js +169 -0
- package/dist/wizard/lib/extensions.d.ts +20 -0
- package/dist/wizard/lib/extensions.js +183 -0
- package/dist/wizard/lib/financial/adapter-factory.d.ts +47 -0
- package/dist/wizard/lib/financial/adapter-factory.js +225 -0
- package/dist/wizard/lib/financial/billing/base.d.ts +6 -0
- package/dist/wizard/lib/financial/billing/base.js +1 -0
- package/dist/wizard/lib/financial/billing/google-billing.d.ts +56 -0
- package/dist/wizard/lib/financial/billing/google-billing.js +298 -0
- package/dist/wizard/lib/financial/billing/meta-billing.d.ts +54 -0
- package/dist/wizard/lib/financial/billing/meta-billing.js +243 -0
- package/dist/wizard/lib/financial/billing/tiktok-billing.d.ts +54 -0
- package/dist/wizard/lib/financial/billing/tiktok-billing.js +260 -0
- package/dist/wizard/lib/financial/campaign/base.d.ts +13 -0
- package/dist/wizard/lib/financial/campaign/base.js +1 -0
- package/dist/wizard/lib/financial/campaign/google-campaign.d.ts +42 -0
- package/dist/wizard/lib/financial/campaign/google-campaign.js +388 -0
- package/dist/wizard/lib/financial/campaign/meta-campaign.d.ts +41 -0
- package/dist/wizard/lib/financial/campaign/meta-campaign.js +311 -0
- package/dist/wizard/lib/financial/campaign/sandbox-campaign.d.ts +45 -0
- package/dist/wizard/lib/financial/campaign/sandbox-campaign.js +261 -0
- package/dist/wizard/lib/financial/campaign/tiktok-campaign.d.ts +40 -0
- package/dist/wizard/lib/financial/campaign/tiktok-campaign.js +350 -0
- package/dist/wizard/lib/financial/funding-auto.d.ts +44 -0
- package/dist/wizard/lib/financial/funding-auto.js +52 -0
- package/dist/wizard/lib/financial/funding-policy.d.ts +60 -0
- package/dist/wizard/lib/financial/funding-policy.js +179 -0
- package/dist/wizard/lib/financial/platform-planner.d.ts +47 -0
- package/dist/wizard/lib/financial/platform-planner.js +134 -0
- package/dist/wizard/lib/financial/reconciliation-engine.d.ts +78 -0
- package/dist/wizard/lib/financial/reconciliation-engine.js +193 -0
- package/dist/wizard/lib/financial/registry.d.ts +22 -0
- package/dist/wizard/lib/financial/registry.js +26 -0
- package/dist/wizard/lib/financial/reporting.d.ts +96 -0
- package/dist/wizard/lib/financial/reporting.js +198 -0
- package/dist/wizard/lib/financial/stablecoin/base.d.ts +6 -0
- package/dist/wizard/lib/financial/stablecoin/base.js +1 -0
- package/dist/wizard/lib/financial/stablecoin/circle.d.ts +54 -0
- package/dist/wizard/lib/financial/stablecoin/circle.js +367 -0
- package/dist/wizard/lib/financial/stablecoin/mercury.d.ts +24 -0
- package/dist/wizard/lib/financial/stablecoin/mercury.js +171 -0
- package/dist/wizard/lib/financial/stablecoin/sandbox-stablecoin.d.ts +47 -0
- package/dist/wizard/lib/financial/stablecoin/sandbox-stablecoin.js +202 -0
- package/dist/wizard/lib/financial/treasury-planner.d.ts +52 -0
- package/dist/wizard/lib/financial/treasury-planner.js +128 -0
- package/dist/wizard/lib/financial-core.d.ts +6 -0
- package/dist/wizard/lib/financial-core.js +5 -0
- package/dist/wizard/lib/financial-vault.d.ts +34 -0
- package/dist/wizard/lib/financial-vault.js +199 -0
- package/dist/wizard/lib/frontmatter.d.ts +30 -0
- package/dist/wizard/lib/frontmatter.js +96 -0
- package/dist/wizard/lib/gap-analysis.d.ts +37 -0
- package/dist/wizard/lib/gap-analysis.js +218 -0
- package/dist/wizard/lib/github.d.ts +22 -0
- package/dist/wizard/lib/github.js +261 -0
- package/dist/wizard/lib/headless-deploy.d.ts +14 -0
- package/dist/wizard/lib/headless-deploy.js +452 -0
- package/dist/wizard/lib/health-monitor.d.ts +15 -0
- package/dist/wizard/lib/health-monitor.js +91 -0
- package/dist/wizard/lib/health-poller.d.ts +9 -0
- package/dist/wizard/lib/health-poller.js +123 -0
- package/dist/wizard/lib/heartbeat.d.ts +15 -0
- package/dist/wizard/lib/heartbeat.js +827 -0
- package/dist/wizard/lib/http-helpers.d.ts +9 -0
- package/dist/wizard/lib/http-helpers.js +24 -0
- package/dist/wizard/lib/image-gen.d.ts +56 -0
- package/dist/wizard/lib/image-gen.js +159 -0
- package/dist/wizard/lib/instance-sizing.d.ts +26 -0
- package/dist/wizard/lib/instance-sizing.js +51 -0
- package/dist/wizard/lib/kongo/analytics.d.ts +29 -0
- package/dist/wizard/lib/kongo/analytics.js +179 -0
- package/dist/wizard/lib/kongo/campaigns.d.ts +52 -0
- package/dist/wizard/lib/kongo/campaigns.js +91 -0
- package/dist/wizard/lib/kongo/client.d.ts +58 -0
- package/dist/wizard/lib/kongo/client.js +221 -0
- package/dist/wizard/lib/kongo/jobs.d.ts +57 -0
- package/dist/wizard/lib/kongo/jobs.js +122 -0
- package/dist/wizard/lib/kongo/pages.d.ts +60 -0
- package/dist/wizard/lib/kongo/pages.js +150 -0
- package/dist/wizard/lib/kongo/provisioner.d.ts +64 -0
- package/dist/wizard/lib/kongo/provisioner.js +116 -0
- package/dist/wizard/lib/kongo/seed.d.ts +49 -0
- package/dist/wizard/lib/kongo/seed.js +237 -0
- package/dist/wizard/lib/kongo/types.d.ts +323 -0
- package/dist/wizard/lib/kongo/types.js +11 -0
- package/dist/wizard/lib/kongo/variants.d.ts +57 -0
- package/dist/wizard/lib/kongo/variants.js +88 -0
- package/dist/wizard/lib/kongo/webhooks.d.ts +41 -0
- package/dist/wizard/lib/kongo/webhooks.js +112 -0
- package/dist/wizard/lib/marker.d.ts +28 -0
- package/dist/wizard/lib/marker.js +79 -0
- package/dist/wizard/lib/migrator.d.ts +35 -0
- package/dist/wizard/lib/migrator.js +190 -0
- package/dist/wizard/lib/natural-language-deploy.d.ts +30 -0
- package/dist/wizard/lib/natural-language-deploy.js +186 -0
- package/dist/wizard/lib/network.d.ts +22 -0
- package/dist/wizard/lib/network.js +72 -0
- package/dist/wizard/lib/oauth-core.d.ts +6 -0
- package/dist/wizard/lib/oauth-core.js +5 -0
- package/dist/wizard/lib/open-browser.d.ts +1 -0
- package/dist/wizard/lib/open-browser.js +26 -0
- package/dist/wizard/lib/patterns/ad-billing-adapter.d.ts +209 -0
- package/dist/wizard/lib/patterns/ad-billing-adapter.js +269 -0
- package/dist/wizard/lib/patterns/ad-platform-adapter.d.ts +200 -0
- package/dist/wizard/lib/patterns/ad-platform-adapter.js +212 -0
- package/dist/wizard/lib/patterns/daemon-process.d.ts +88 -0
- package/dist/wizard/lib/patterns/daemon-process.js +271 -0
- package/dist/wizard/lib/patterns/financial-transaction.d.ts +161 -0
- package/dist/wizard/lib/patterns/financial-transaction.js +132 -0
- package/dist/wizard/lib/patterns/funding-plan.d.ts +136 -0
- package/dist/wizard/lib/patterns/funding-plan.js +200 -0
- package/dist/wizard/lib/patterns/oauth-token-lifecycle.d.ts +94 -0
- package/dist/wizard/lib/patterns/oauth-token-lifecycle.js +139 -0
- package/dist/wizard/lib/patterns/outbound-rate-limiter.d.ts +67 -0
- package/dist/wizard/lib/patterns/outbound-rate-limiter.js +216 -0
- package/dist/wizard/lib/patterns/revenue-source-adapter.d.ts +96 -0
- package/dist/wizard/lib/patterns/revenue-source-adapter.js +182 -0
- package/dist/wizard/lib/patterns/stablecoin-adapter.d.ts +218 -0
- package/dist/wizard/lib/patterns/stablecoin-adapter.js +264 -0
- package/dist/wizard/lib/prd-validator.d.ts +39 -0
- package/dist/wizard/lib/prd-validator.js +137 -0
- package/dist/wizard/lib/project-init.d.ts +24 -0
- package/dist/wizard/lib/project-init.js +193 -0
- package/dist/wizard/lib/project-registry.d.ts +86 -0
- package/dist/wizard/lib/project-registry.js +359 -0
- package/dist/wizard/lib/provision-manifest.d.ts +44 -0
- package/dist/wizard/lib/provision-manifest.js +164 -0
- package/dist/wizard/lib/provisioner-registry.d.ts +15 -0
- package/dist/wizard/lib/provisioner-registry.js +34 -0
- package/dist/wizard/lib/provisioners/aws-vps.d.ts +6 -0
- package/dist/wizard/lib/provisioners/aws-vps.js +643 -0
- package/dist/wizard/lib/provisioners/cloudflare.d.ts +6 -0
- package/dist/wizard/lib/provisioners/cloudflare.js +300 -0
- package/dist/wizard/lib/provisioners/docker.d.ts +6 -0
- package/dist/wizard/lib/provisioners/docker.js +75 -0
- package/dist/wizard/lib/provisioners/http-client.d.ts +20 -0
- package/dist/wizard/lib/provisioners/http-client.js +79 -0
- package/dist/wizard/lib/provisioners/railway.d.ts +6 -0
- package/dist/wizard/lib/provisioners/railway.js +413 -0
- package/dist/wizard/lib/provisioners/scripts/caddyfile.d.ts +10 -0
- package/dist/wizard/lib/provisioners/scripts/caddyfile.js +54 -0
- package/dist/wizard/lib/provisioners/scripts/deploy-vps.d.ts +10 -0
- package/dist/wizard/lib/provisioners/scripts/deploy-vps.js +112 -0
- package/dist/wizard/lib/provisioners/scripts/docker-compose.d.ts +11 -0
- package/dist/wizard/lib/provisioners/scripts/docker-compose.js +91 -0
- package/dist/wizard/lib/provisioners/scripts/dockerfile.d.ts +5 -0
- package/dist/wizard/lib/provisioners/scripts/dockerfile.js +185 -0
- package/dist/wizard/lib/provisioners/scripts/ecosystem-config.d.ts +10 -0
- package/dist/wizard/lib/provisioners/scripts/ecosystem-config.js +36 -0
- package/dist/wizard/lib/provisioners/scripts/provision-vps.d.ts +14 -0
- package/dist/wizard/lib/provisioners/scripts/provision-vps.js +202 -0
- package/dist/wizard/lib/provisioners/scripts/rollback-vps.d.ts +10 -0
- package/dist/wizard/lib/provisioners/scripts/rollback-vps.js +67 -0
- package/dist/wizard/lib/provisioners/self-deploy.d.ts +41 -0
- package/dist/wizard/lib/provisioners/self-deploy.js +185 -0
- package/dist/wizard/lib/provisioners/static-s3.d.ts +6 -0
- package/dist/wizard/lib/provisioners/static-s3.js +235 -0
- package/dist/wizard/lib/provisioners/types.d.ts +40 -0
- package/dist/wizard/lib/provisioners/types.js +4 -0
- package/dist/wizard/lib/provisioners/vercel.d.ts +6 -0
- package/dist/wizard/lib/provisioners/vercel.js +287 -0
- package/dist/wizard/lib/pty-manager.d.ts +42 -0
- package/dist/wizard/lib/pty-manager.js +231 -0
- package/dist/wizard/lib/rate-limiter-core.d.ts +5 -0
- package/dist/wizard/lib/rate-limiter-core.js +5 -0
- package/dist/wizard/lib/reconciliation.d.ts +43 -0
- package/dist/wizard/lib/reconciliation.js +173 -0
- package/dist/wizard/lib/revenue-types.d.ts +5 -0
- package/dist/wizard/lib/revenue-types.js +1 -0
- package/dist/wizard/lib/route-optimizer.d.ts +28 -0
- package/dist/wizard/lib/route-optimizer.js +93 -0
- package/dist/wizard/lib/s3-deploy.d.ts +19 -0
- package/dist/wizard/lib/s3-deploy.js +156 -0
- package/dist/wizard/lib/safety-tiers.d.ts +76 -0
- package/dist/wizard/lib/safety-tiers.js +134 -0
- package/dist/wizard/lib/sentry-generator.d.ts +15 -0
- package/dist/wizard/lib/sentry-generator.js +116 -0
- package/dist/wizard/lib/server-config.d.ts +13 -0
- package/dist/wizard/lib/server-config.js +23 -0
- package/dist/wizard/lib/service-install.d.ts +18 -0
- package/dist/wizard/lib/service-install.js +182 -0
- package/dist/wizard/lib/site-scanner.d.ts +80 -0
- package/dist/wizard/lib/site-scanner.js +262 -0
- package/dist/wizard/lib/ssh-deploy.d.ts +25 -0
- package/dist/wizard/lib/ssh-deploy.js +225 -0
- package/dist/wizard/lib/templates.d.ts +24 -0
- package/dist/wizard/lib/templates.js +219 -0
- package/dist/wizard/lib/totp.d.ts +35 -0
- package/dist/wizard/lib/totp.js +276 -0
- package/dist/wizard/lib/tower-auth.d.ts +43 -0
- package/dist/wizard/lib/tower-auth.js +352 -0
- package/dist/wizard/lib/tower-rate-limit.d.ts +14 -0
- package/dist/wizard/lib/tower-rate-limit.js +61 -0
- package/dist/wizard/lib/tower-session.d.ts +28 -0
- package/dist/wizard/lib/tower-session.js +119 -0
- package/dist/wizard/lib/treasury-backup.d.ts +23 -0
- package/dist/wizard/lib/treasury-backup.js +126 -0
- package/dist/wizard/lib/treasury-heartbeat.d.ts +82 -0
- package/dist/wizard/lib/treasury-heartbeat.js +1104 -0
- package/dist/wizard/lib/updater.d.ts +29 -0
- package/dist/wizard/lib/updater.js +190 -0
- package/dist/wizard/lib/user-manager.d.ts +39 -0
- package/dist/wizard/lib/user-manager.js +182 -0
- package/dist/wizard/lib/vault.d.ts +26 -0
- package/dist/wizard/lib/vault.js +161 -0
- package/dist/wizard/router.d.ts +5 -0
- package/dist/wizard/router.js +15 -0
- package/dist/wizard/server.d.ts +18 -0
- package/dist/wizard/server.js +436 -0
- package/package.json +59 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Startup environment validation script generator (ADR-018).
|
|
3
|
+
* Generates a script that checks required env vars at boot time.
|
|
4
|
+
* No dependencies — pure Node.js stdlib or Python stdlib.
|
|
5
|
+
*/
|
|
6
|
+
import { writeFile, readFile } from 'node:fs/promises';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
/** Patterns that indicate a placeholder value, not a real value. */
|
|
9
|
+
const PLACEHOLDER_PATTERNS = [
|
|
10
|
+
/^#\s*pending/i,
|
|
11
|
+
/^#\s*check/i,
|
|
12
|
+
/^your[-_]?\w+[-_]?here$/i,
|
|
13
|
+
/^TODO/i,
|
|
14
|
+
/^CHANGE[-_]?ME/i,
|
|
15
|
+
/^xxx/i,
|
|
16
|
+
/^\s*$/,
|
|
17
|
+
];
|
|
18
|
+
function isPlaceholder(value) {
|
|
19
|
+
return PLACEHOLDER_PATTERNS.some(p => p.test(value));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Parse a .env file and extract variable names and whether their values are real.
|
|
23
|
+
* Comments-only lines and section headers are skipped.
|
|
24
|
+
*/
|
|
25
|
+
function parseEnvFile(content) {
|
|
26
|
+
const vars = [];
|
|
27
|
+
for (const line of content.split('\n')) {
|
|
28
|
+
const trimmed = line.trim();
|
|
29
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
30
|
+
continue;
|
|
31
|
+
const eqIdx = trimmed.indexOf('=');
|
|
32
|
+
if (eqIdx < 1)
|
|
33
|
+
continue;
|
|
34
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
35
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
36
|
+
// Strip matching quotes only (both must be the same character)
|
|
37
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
38
|
+
value = value.slice(1, -1);
|
|
39
|
+
}
|
|
40
|
+
// Skip VoidForge metadata keys
|
|
41
|
+
if (key.startsWith('VERCEL_PROJECT_') || key.startsWith('CF_PROJECT_') || key.startsWith('RAILWAY_PROJECT_'))
|
|
42
|
+
continue;
|
|
43
|
+
vars.push({ key, hasValue: !isPlaceholder(value) });
|
|
44
|
+
}
|
|
45
|
+
return vars;
|
|
46
|
+
}
|
|
47
|
+
function generateNodeValidator(vars) {
|
|
48
|
+
const requiredKeys = vars.map(v => ` '${v.key}'`).join(',\n');
|
|
49
|
+
const pendingKeys = vars.filter(v => !v.hasValue).map(v => ` '${v.key}'`).join(',\n');
|
|
50
|
+
return `#!/usr/bin/env node
|
|
51
|
+
/**
|
|
52
|
+
* Startup environment validation — checks required env vars before boot.
|
|
53
|
+
* Generated by VoidForge (ADR-018).
|
|
54
|
+
* Usage: node validate-env.js
|
|
55
|
+
* Works in both CommonJS and ESM projects.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
const required = [
|
|
59
|
+
${requiredKeys}
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const PLACEHOLDER_PATTERNS = [
|
|
63
|
+
/^#\\s*pending/i,
|
|
64
|
+
/^#\\s*check/i,
|
|
65
|
+
/^your[-_]?\\w+[-_]?here$/i,
|
|
66
|
+
/^TODO/i,
|
|
67
|
+
/^CHANGE[-_]?ME/i,
|
|
68
|
+
/^xxx/i,
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
function isPlaceholder(val) {
|
|
72
|
+
return !val.trim() || PLACEHOLDER_PATTERNS.some(p => p.test(val));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function validateEnv() {
|
|
76
|
+
const missing = [];
|
|
77
|
+
const empty = [];
|
|
78
|
+
|
|
79
|
+
for (const key of required) {
|
|
80
|
+
const val = process.env[key];
|
|
81
|
+
if (val === undefined) {
|
|
82
|
+
missing.push(key);
|
|
83
|
+
} else if (isPlaceholder(val)) {
|
|
84
|
+
empty.push(key);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (missing.length > 0 || empty.length > 0) {
|
|
89
|
+
console.error('\\n=== Environment Validation Failed ===\\n');
|
|
90
|
+
if (missing.length > 0) {
|
|
91
|
+
console.error('Missing variables:');
|
|
92
|
+
for (const key of missing) console.error(' - ' + key);
|
|
93
|
+
}
|
|
94
|
+
if (empty.length > 0) {
|
|
95
|
+
console.error('Placeholder values (need real values):');
|
|
96
|
+
for (const key of empty) console.error(' - ' + key + ' (placeholder value detected)');
|
|
97
|
+
}
|
|
98
|
+
console.error('\\nSet these in your .env file or environment before starting the app.\\n');
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log('Environment validation passed (' + required.length + ' vars checked)');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Run if called directly — works in both CJS and ESM
|
|
106
|
+
const isMain = typeof require !== 'undefined'
|
|
107
|
+
? require.main === module
|
|
108
|
+
: process.argv[1]?.endsWith('validate-env.js');
|
|
109
|
+
|
|
110
|
+
if (isMain) {
|
|
111
|
+
// Load .env if dotenv is available
|
|
112
|
+
try { require('dotenv').config(); } catch { /* dotenv not installed — using process.env */ }
|
|
113
|
+
validateEnv();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Export for programmatic use
|
|
117
|
+
if (typeof module !== 'undefined') module.exports = { validateEnv };
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
120
|
+
function generatePythonValidator(vars) {
|
|
121
|
+
const requiredList = vars.map(v => ` "${v.key}"`).join(',\n');
|
|
122
|
+
return `#!/usr/bin/env python3
|
|
123
|
+
"""
|
|
124
|
+
Startup environment validation — checks required env vars before boot.
|
|
125
|
+
Generated by VoidForge (ADR-018).
|
|
126
|
+
Usage: python validate_env.py
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
import os
|
|
130
|
+
import sys
|
|
131
|
+
|
|
132
|
+
REQUIRED = [
|
|
133
|
+
${requiredList}
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
PLACEHOLDER_PREFIXES = ["# pending", "# check", "TODO", "CHANGE_ME", "xxx"]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def validate_env():
|
|
140
|
+
missing = []
|
|
141
|
+
empty = []
|
|
142
|
+
|
|
143
|
+
for key in REQUIRED:
|
|
144
|
+
val = os.environ.get(key)
|
|
145
|
+
if val is None:
|
|
146
|
+
missing.append(key)
|
|
147
|
+
elif not val.strip() or any(val.startswith(p) for p in PLACEHOLDER_PREFIXES):
|
|
148
|
+
empty.append(key)
|
|
149
|
+
|
|
150
|
+
if missing or empty:
|
|
151
|
+
print("\\n=== Environment Validation Failed ===\\n", file=sys.stderr)
|
|
152
|
+
if missing:
|
|
153
|
+
print("Missing variables:", file=sys.stderr)
|
|
154
|
+
for key in missing:
|
|
155
|
+
print(f" - {key}", file=sys.stderr)
|
|
156
|
+
if empty:
|
|
157
|
+
print("Placeholder values (need real values):", file=sys.stderr)
|
|
158
|
+
for key in empty:
|
|
159
|
+
print(f" - {key} (placeholder value detected)", file=sys.stderr)
|
|
160
|
+
print("\\nSet these in your .env file or environment before starting the app.\\n", file=sys.stderr)
|
|
161
|
+
sys.exit(1)
|
|
162
|
+
|
|
163
|
+
print(f"Environment validation passed ({len(REQUIRED)} vars checked)")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
if __name__ == "__main__":
|
|
167
|
+
# Load .env if python-dotenv is available
|
|
168
|
+
try:
|
|
169
|
+
from dotenv import load_dotenv
|
|
170
|
+
load_dotenv()
|
|
171
|
+
except ImportError:
|
|
172
|
+
pass
|
|
173
|
+
validate_env()
|
|
174
|
+
`;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Generate an environment validation script based on the project's .env file.
|
|
178
|
+
*/
|
|
179
|
+
export async function generateEnvValidator(projectDir, framework) {
|
|
180
|
+
const isPython = framework === 'django' || framework === 'flask';
|
|
181
|
+
const filename = isPython ? 'validate_env.py' : 'validate-env.js';
|
|
182
|
+
try {
|
|
183
|
+
// Read the current .env file to discover required variables
|
|
184
|
+
let envContent = '';
|
|
185
|
+
try {
|
|
186
|
+
envContent = await readFile(join(projectDir, '.env'), 'utf-8');
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// No .env file — generate a minimal validator
|
|
190
|
+
return { success: true, file: '' };
|
|
191
|
+
}
|
|
192
|
+
const vars = parseEnvFile(envContent);
|
|
193
|
+
if (vars.length === 0) {
|
|
194
|
+
return { success: true, file: '' };
|
|
195
|
+
}
|
|
196
|
+
const content = isPython
|
|
197
|
+
? generatePythonValidator(vars)
|
|
198
|
+
: generateNodeValidator(vars);
|
|
199
|
+
await writeFile(join(projectDir, filename), content, 'utf-8');
|
|
200
|
+
return { success: true, file: filename };
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
return { success: false, file: filename, error: err.message };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared .env file writer — used by all provisioners.
|
|
3
|
+
* Appends a labeled section to the project's .env file.
|
|
4
|
+
* Extracted from 5 identical copy-pasted implementations.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Append a section of environment variables to a project's .env file.
|
|
8
|
+
* Creates the file if it doesn't exist. Adds a blank line separator.
|
|
9
|
+
*
|
|
10
|
+
* WARNING: Not safe for concurrent calls. All provisioner steps run sequentially
|
|
11
|
+
* through the SSE stream, so this is safe in practice. Do not call in parallel.
|
|
12
|
+
*/
|
|
13
|
+
export declare function appendEnvSection(projectDir: string, lines: string[]): Promise<void>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared .env file writer — used by all provisioners.
|
|
3
|
+
* Appends a labeled section to the project's .env file.
|
|
4
|
+
* Extracted from 5 identical copy-pasted implementations.
|
|
5
|
+
*/
|
|
6
|
+
import { readFile, writeFile, chmod } from 'node:fs/promises';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
/**
|
|
9
|
+
* Append a section of environment variables to a project's .env file.
|
|
10
|
+
* Creates the file if it doesn't exist. Adds a blank line separator.
|
|
11
|
+
*
|
|
12
|
+
* WARNING: Not safe for concurrent calls. All provisioner steps run sequentially
|
|
13
|
+
* through the SSE stream, so this is safe in practice. Do not call in parallel.
|
|
14
|
+
*/
|
|
15
|
+
export async function appendEnvSection(projectDir, lines) {
|
|
16
|
+
const envPath = join(projectDir, '.env');
|
|
17
|
+
let existing = '';
|
|
18
|
+
try {
|
|
19
|
+
existing = await readFile(envPath, 'utf-8');
|
|
20
|
+
}
|
|
21
|
+
catch { /* new file */ }
|
|
22
|
+
const separator = existing ? '\n\n' : '';
|
|
23
|
+
await writeFile(envPath, existing + separator + lines.join('\n') + '\n', 'utf-8');
|
|
24
|
+
// Restrict .env permissions — credentials should not be world-readable
|
|
25
|
+
await chmod(envPath, 0o600);
|
|
26
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared child process utility — timeout, abort signal, streaming.
|
|
3
|
+
* Used by github.ts (git commands) and ssh-deploy.ts (ssh/rsync).
|
|
4
|
+
* Uses execFile (not exec) to prevent shell injection. (ADR-013)
|
|
5
|
+
*/
|
|
6
|
+
interface ExecResult {
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
}
|
|
10
|
+
interface ExecOptions {
|
|
11
|
+
cwd?: string;
|
|
12
|
+
timeout?: number;
|
|
13
|
+
env?: Record<string, string | undefined>;
|
|
14
|
+
abortSignal?: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Execute a command with timeout and abort support.
|
|
18
|
+
* Uses execFile (not exec) — no shell, no injection risk.
|
|
19
|
+
*/
|
|
20
|
+
export declare function execCommand(command: string, args: string[], options?: ExecOptions): Promise<ExecResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Check if a binary is available on the system.
|
|
23
|
+
* Returns the path if found, null if not.
|
|
24
|
+
*/
|
|
25
|
+
export declare function whichBinary(name: string): Promise<string | null>;
|
|
26
|
+
/**
|
|
27
|
+
* Validate that required binaries exist. Returns missing binary names.
|
|
28
|
+
*/
|
|
29
|
+
export declare function validateBinaries(names: string[]): Promise<string[]>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared child process utility — timeout, abort signal, streaming.
|
|
3
|
+
* Used by github.ts (git commands) and ssh-deploy.ts (ssh/rsync).
|
|
4
|
+
* Uses execFile (not exec) to prevent shell injection. (ADR-013)
|
|
5
|
+
*/
|
|
6
|
+
import { execFile as execFileCb } from 'node:child_process';
|
|
7
|
+
import { promisify } from 'node:util';
|
|
8
|
+
const execFileAsync = promisify(execFileCb);
|
|
9
|
+
const DEFAULT_TIMEOUT_MS = 120_000; // 2 minutes
|
|
10
|
+
/**
|
|
11
|
+
* Execute a command with timeout and abort support.
|
|
12
|
+
* Uses execFile (not exec) — no shell, no injection risk.
|
|
13
|
+
*/
|
|
14
|
+
export async function execCommand(command, args, options) {
|
|
15
|
+
const timeout = options?.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
16
|
+
const result = await execFileAsync(command, args, {
|
|
17
|
+
cwd: options?.cwd,
|
|
18
|
+
timeout,
|
|
19
|
+
env: options?.env ? { ...process.env, ...options.env } : undefined,
|
|
20
|
+
signal: options?.abortSignal,
|
|
21
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
stdout: result.stdout?.toString() ?? '',
|
|
25
|
+
stderr: result.stderr?.toString() ?? '',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if a binary is available on the system.
|
|
30
|
+
* Returns the path if found, null if not.
|
|
31
|
+
*/
|
|
32
|
+
export async function whichBinary(name) {
|
|
33
|
+
try {
|
|
34
|
+
const { stdout } = await execCommand('which', [name], { timeout: 5000 });
|
|
35
|
+
return stdout.trim() || null;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validate that required binaries exist. Returns missing binary names.
|
|
43
|
+
*/
|
|
44
|
+
export async function validateBinaries(names) {
|
|
45
|
+
const missing = [];
|
|
46
|
+
for (const name of names) {
|
|
47
|
+
const found = await whichBinary(name);
|
|
48
|
+
if (!found)
|
|
49
|
+
missing.push(name);
|
|
50
|
+
}
|
|
51
|
+
return missing;
|
|
52
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Methodology A/B Testing — run two protocol variants on the same code and compare.
|
|
3
|
+
*
|
|
4
|
+
* Tracks per-agent true-positive rates across projects. Over time, tunes the
|
|
5
|
+
* methodology based on data, not intuition. Wong manages the experiments.
|
|
6
|
+
*
|
|
7
|
+
* Storage: ~/.voidforge/experiments.json
|
|
8
|
+
*/
|
|
9
|
+
export interface ExperimentVariant {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
agentCount: number;
|
|
13
|
+
phases: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface ExperimentResult {
|
|
16
|
+
variantName: string;
|
|
17
|
+
findings: number;
|
|
18
|
+
truePositives: number;
|
|
19
|
+
falsePositives: number;
|
|
20
|
+
contextTokens: number;
|
|
21
|
+
durationMs: number;
|
|
22
|
+
agentResults: AgentResult[];
|
|
23
|
+
}
|
|
24
|
+
export interface AgentResult {
|
|
25
|
+
agent: string;
|
|
26
|
+
universe: string;
|
|
27
|
+
findings: number;
|
|
28
|
+
truePositives: number;
|
|
29
|
+
falsePositives: number;
|
|
30
|
+
confidence: number;
|
|
31
|
+
}
|
|
32
|
+
export type ExperimentStatus = 'planned' | 'running' | 'complete' | 'cancelled';
|
|
33
|
+
export interface Experiment {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
project: string;
|
|
38
|
+
domain: string;
|
|
39
|
+
status: ExperimentStatus;
|
|
40
|
+
createdAt: string;
|
|
41
|
+
completedAt: string | null;
|
|
42
|
+
variantA: ExperimentVariant;
|
|
43
|
+
variantB: ExperimentVariant;
|
|
44
|
+
resultA: ExperimentResult | null;
|
|
45
|
+
resultB: ExperimentResult | null;
|
|
46
|
+
winner: 'A' | 'B' | 'tie' | null;
|
|
47
|
+
winReason: string | null;
|
|
48
|
+
}
|
|
49
|
+
export interface ExperimentStore {
|
|
50
|
+
version: 1;
|
|
51
|
+
experiments: Experiment[];
|
|
52
|
+
}
|
|
53
|
+
/** Create a new experiment. */
|
|
54
|
+
export declare function createExperiment(name: string, description: string, project: string, domain: string, variantA: ExperimentVariant, variantB: ExperimentVariant): Promise<Experiment>;
|
|
55
|
+
/** Record the result of running a variant. */
|
|
56
|
+
export declare function recordResult(experimentId: string, variant: 'A' | 'B', result: ExperimentResult): Promise<Experiment | null>;
|
|
57
|
+
/** List all experiments, optionally filtered by status or project. */
|
|
58
|
+
export declare function listExperiments(filter?: {
|
|
59
|
+
status?: ExperimentStatus;
|
|
60
|
+
project?: string;
|
|
61
|
+
}): Promise<Experiment[]>;
|
|
62
|
+
/** Get a single experiment by ID. */
|
|
63
|
+
export declare function getExperiment(id: string): Promise<Experiment | null>;
|
|
64
|
+
/** Get aggregate per-agent accuracy stats across all completed experiments. */
|
|
65
|
+
export declare function getAgentStats(): Promise<Map<string, {
|
|
66
|
+
experiments: number;
|
|
67
|
+
truePositives: number;
|
|
68
|
+
falsePositives: number;
|
|
69
|
+
avgConfidence: number;
|
|
70
|
+
}>>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Methodology A/B Testing — run two protocol variants on the same code and compare.
|
|
3
|
+
*
|
|
4
|
+
* Tracks per-agent true-positive rates across projects. Over time, tunes the
|
|
5
|
+
* methodology based on data, not intuition. Wong manages the experiments.
|
|
6
|
+
*
|
|
7
|
+
* Storage: ~/.voidforge/experiments.json
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, mkdir, rename, open } from 'node:fs/promises';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { randomUUID } from 'node:crypto';
|
|
13
|
+
const EXPERIMENTS_DIR = join(homedir(), '.voidforge');
|
|
14
|
+
const EXPERIMENTS_FILE = join(EXPERIMENTS_DIR, 'experiments.json');
|
|
15
|
+
// ── Storage ─────────────────────────────────────
|
|
16
|
+
async function ensureDir() {
|
|
17
|
+
try {
|
|
18
|
+
await mkdir(EXPERIMENTS_DIR, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
catch { /* exists */ }
|
|
21
|
+
}
|
|
22
|
+
async function readStore() {
|
|
23
|
+
try {
|
|
24
|
+
const content = await readFile(EXPERIMENTS_FILE, 'utf-8');
|
|
25
|
+
const data = JSON.parse(content);
|
|
26
|
+
if (data.version === 1 && Array.isArray(data.experiments))
|
|
27
|
+
return data;
|
|
28
|
+
}
|
|
29
|
+
catch { /* missing or corrupt */ }
|
|
30
|
+
return { version: 1, experiments: [] };
|
|
31
|
+
}
|
|
32
|
+
// IG-R4 LOKI-002: Serialization queue prevents concurrent write corruption
|
|
33
|
+
let writeQueue = Promise.resolve();
|
|
34
|
+
function serialized(fn) {
|
|
35
|
+
const result = writeQueue.then(fn, () => fn());
|
|
36
|
+
writeQueue = result.then(() => { }, () => { });
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
async function writeStore(store) {
|
|
40
|
+
await ensureDir();
|
|
41
|
+
// IG-R4: Atomic write with fsync — temp+sync+rename
|
|
42
|
+
const tmpFile = EXPERIMENTS_FILE + '.tmp';
|
|
43
|
+
const fh = await open(tmpFile, 'w', 0o600);
|
|
44
|
+
try {
|
|
45
|
+
await fh.writeFile(JSON.stringify(store, null, 2));
|
|
46
|
+
await fh.sync();
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
await fh.close();
|
|
50
|
+
}
|
|
51
|
+
await rename(tmpFile, EXPERIMENTS_FILE);
|
|
52
|
+
}
|
|
53
|
+
// ── API ─────────────────────────────────────────
|
|
54
|
+
/** Create a new experiment. */
|
|
55
|
+
export function createExperiment(name, description, project, domain, variantA, variantB) {
|
|
56
|
+
return serialized(async () => {
|
|
57
|
+
const store = await readStore();
|
|
58
|
+
const experiment = {
|
|
59
|
+
id: randomUUID(),
|
|
60
|
+
name,
|
|
61
|
+
description,
|
|
62
|
+
project,
|
|
63
|
+
domain,
|
|
64
|
+
status: 'planned',
|
|
65
|
+
createdAt: new Date().toISOString(),
|
|
66
|
+
completedAt: null,
|
|
67
|
+
variantA,
|
|
68
|
+
variantB,
|
|
69
|
+
resultA: null,
|
|
70
|
+
resultB: null,
|
|
71
|
+
winner: null,
|
|
72
|
+
winReason: null,
|
|
73
|
+
};
|
|
74
|
+
store.experiments.push(experiment);
|
|
75
|
+
await writeStore(store);
|
|
76
|
+
return experiment;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/** Record the result of running a variant. */
|
|
80
|
+
export function recordResult(experimentId, variant, result) {
|
|
81
|
+
return serialized(async () => {
|
|
82
|
+
const store = await readStore();
|
|
83
|
+
const experiment = store.experiments.find(e => e.id === experimentId);
|
|
84
|
+
if (!experiment)
|
|
85
|
+
return null;
|
|
86
|
+
if (variant === 'A')
|
|
87
|
+
experiment.resultA = result;
|
|
88
|
+
else
|
|
89
|
+
experiment.resultB = result;
|
|
90
|
+
// Auto-evaluate if both results are in
|
|
91
|
+
if (experiment.resultA && experiment.resultB) {
|
|
92
|
+
experiment.status = 'complete';
|
|
93
|
+
experiment.completedAt = new Date().toISOString();
|
|
94
|
+
const evaluation = evaluate(experiment.resultA, experiment.resultB);
|
|
95
|
+
experiment.winner = evaluation.winner;
|
|
96
|
+
experiment.winReason = evaluation.reason;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
experiment.status = 'running';
|
|
100
|
+
}
|
|
101
|
+
await writeStore(store);
|
|
102
|
+
return experiment;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/** List all experiments, optionally filtered by status or project. */
|
|
106
|
+
export async function listExperiments(filter) {
|
|
107
|
+
const store = await readStore();
|
|
108
|
+
let results = store.experiments;
|
|
109
|
+
if (filter?.status)
|
|
110
|
+
results = results.filter(e => e.status === filter.status);
|
|
111
|
+
if (filter?.project)
|
|
112
|
+
results = results.filter(e => e.project === filter.project);
|
|
113
|
+
return results;
|
|
114
|
+
}
|
|
115
|
+
/** Get a single experiment by ID. */
|
|
116
|
+
export async function getExperiment(id) {
|
|
117
|
+
const store = await readStore();
|
|
118
|
+
return store.experiments.find(e => e.id === id) ?? null;
|
|
119
|
+
}
|
|
120
|
+
/** Get aggregate per-agent accuracy stats across all completed experiments. */
|
|
121
|
+
export async function getAgentStats() {
|
|
122
|
+
const store = await readStore();
|
|
123
|
+
const stats = new Map();
|
|
124
|
+
for (const exp of store.experiments) {
|
|
125
|
+
if (exp.status !== 'complete')
|
|
126
|
+
continue;
|
|
127
|
+
const allResults = [exp.resultA, exp.resultB].filter(Boolean);
|
|
128
|
+
for (const result of allResults) {
|
|
129
|
+
for (const ar of result.agentResults) {
|
|
130
|
+
const existing = stats.get(ar.agent) ?? { experiments: 0, truePositives: 0, falsePositives: 0, totalConfidence: 0 };
|
|
131
|
+
existing.experiments++;
|
|
132
|
+
existing.truePositives += ar.truePositives;
|
|
133
|
+
existing.falsePositives += ar.falsePositives;
|
|
134
|
+
existing.totalConfidence += ar.confidence;
|
|
135
|
+
stats.set(ar.agent, existing);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const result = new Map();
|
|
140
|
+
for (const [agent, s] of stats) {
|
|
141
|
+
result.set(agent, {
|
|
142
|
+
experiments: s.experiments,
|
|
143
|
+
truePositives: s.truePositives,
|
|
144
|
+
falsePositives: s.falsePositives,
|
|
145
|
+
avgConfidence: s.experiments > 0 ? Math.round(s.totalConfidence / s.experiments) : 0,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
function evaluate(resultA, resultB) {
|
|
151
|
+
// Primary metric: true positive rate (findings that are real issues)
|
|
152
|
+
const tprA = resultA.findings > 0 ? resultA.truePositives / resultA.findings : 0;
|
|
153
|
+
const tprB = resultB.findings > 0 ? resultB.truePositives / resultB.findings : 0;
|
|
154
|
+
// Secondary metric: context efficiency (true positives per 1000 tokens)
|
|
155
|
+
const effA = resultA.contextTokens > 0 ? (resultA.truePositives / resultA.contextTokens) * 1000 : 0;
|
|
156
|
+
const effB = resultB.contextTokens > 0 ? (resultB.truePositives / resultB.contextTokens) * 1000 : 0;
|
|
157
|
+
const tprDiff = Math.abs(tprA - tprB);
|
|
158
|
+
const SIGNIFICANCE_THRESHOLD = 0.1; // 10% difference is significant
|
|
159
|
+
if (tprDiff < SIGNIFICANCE_THRESHOLD) {
|
|
160
|
+
// Similar accuracy — decide on efficiency
|
|
161
|
+
if (Math.abs(effA - effB) < 0.01) {
|
|
162
|
+
return { winner: 'tie', reason: `Similar accuracy (${(tprA * 100).toFixed(0)}% vs ${(tprB * 100).toFixed(0)}%) and similar context efficiency` };
|
|
163
|
+
}
|
|
164
|
+
const moreEfficient = effA > effB ? 'A' : 'B';
|
|
165
|
+
return { winner: moreEfficient, reason: `Similar accuracy but ${moreEfficient} is more context-efficient (${(effA * 100).toFixed(1)} vs ${(effB * 100).toFixed(1)} TP/1k tokens)` };
|
|
166
|
+
}
|
|
167
|
+
const moreAccurate = tprA > tprB ? 'A' : 'B';
|
|
168
|
+
return { winner: moreAccurate, reason: `${moreAccurate} has higher true-positive rate (${((moreAccurate === 'A' ? tprA : tprB) * 100).toFixed(0)}% vs ${((moreAccurate === 'A' ? tprB : tprA) * 100).toFixed(0)}%)` };
|
|
169
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension system — install, uninstall, and list extensions for VoidForge projects.
|
|
3
|
+
*
|
|
4
|
+
* Extensions add optional capabilities to projects without npm dependencies.
|
|
5
|
+
* Templates are copied into the project; runtime imports come from the global wizard.
|
|
6
|
+
*/
|
|
7
|
+
export type ExtensionName = 'danger-room' | 'cultivation';
|
|
8
|
+
interface ExtensionDef {
|
|
9
|
+
name: ExtensionName;
|
|
10
|
+
description: string;
|
|
11
|
+
install: (projectDir: string) => Promise<number>;
|
|
12
|
+
uninstall: (projectDir: string) => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare function getExtension(name: string): ExtensionDef | undefined;
|
|
15
|
+
export declare function listExtensions(): ExtensionDef[];
|
|
16
|
+
export declare function installExtension(projectDir: string, name: string): Promise<{
|
|
17
|
+
filesCreated: number;
|
|
18
|
+
}>;
|
|
19
|
+
export declare function uninstallExtension(projectDir: string, name: string): Promise<void>;
|
|
20
|
+
export {};
|