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,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Financial Vault — Separate encrypted storage for ad platform and bank credentials.
|
|
3
|
+
*
|
|
4
|
+
* Key differences from the infrastructure vault (vault.ts):
|
|
5
|
+
* - Stored at ~/.voidforge/treasury/vault.enc (not ~/.voidforge/vault.enc)
|
|
6
|
+
* - Uses scrypt key derivation (memory-hard, unlike PBKDF2 in the infra vault)
|
|
7
|
+
* Note: PRD specifies Argon2id but Node.js has no built-in Argon2id.
|
|
8
|
+
* scrypt is the closest built-in memory-hard KDF. Zero-dependency constraint wins.
|
|
9
|
+
* - Separate password from the infrastructure vault
|
|
10
|
+
* - TOTP 2FA required for write operations (see totp.ts)
|
|
11
|
+
*
|
|
12
|
+
* PRD Reference: §9.11 (Financial Security), ADR-4 (TOTP storage), §9.18 (macOS fsync)
|
|
13
|
+
*
|
|
14
|
+
* Threat model: compromise of the infrastructure vault should NOT automatically
|
|
15
|
+
* compromise the financial vault. Different password, different file, different KDF.
|
|
16
|
+
*/
|
|
17
|
+
import { createCipheriv, createDecipheriv, scrypt, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
18
|
+
import { readFile, mkdir, open, rename } from 'node:fs/promises';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
import { existsSync } from 'node:fs';
|
|
21
|
+
import { homedir, platform } from 'node:os';
|
|
22
|
+
const TREASURY_DIR = join(homedir(), '.voidforge', 'treasury');
|
|
23
|
+
const VAULT_PATH = join(TREASURY_DIR, 'vault.enc');
|
|
24
|
+
const ALGORITHM = 'aes-256-gcm';
|
|
25
|
+
const KEY_LENGTH = 32;
|
|
26
|
+
const IV_LENGTH = 16;
|
|
27
|
+
const SALT_LENGTH = 32;
|
|
28
|
+
const AUTH_TAG_LENGTH = 16;
|
|
29
|
+
const MAX_PASSWORD_LENGTH = 256;
|
|
30
|
+
// scrypt parameters — memory-hard to resist brute force
|
|
31
|
+
// N=2^17 (131072), r=8, p=1 — ~128MB memory, ~1s on modern hardware
|
|
32
|
+
const SCRYPT_N = 131072;
|
|
33
|
+
const SCRYPT_R = 8;
|
|
34
|
+
const SCRYPT_P = 1;
|
|
35
|
+
/** In-memory cache — zeroed on lock or vault timeout */
|
|
36
|
+
let sessionCache = null;
|
|
37
|
+
/** Write queue to serialize vault operations */
|
|
38
|
+
let writeQueue = Promise.resolve();
|
|
39
|
+
function serialized(fn) {
|
|
40
|
+
const result = writeQueue.then(fn, () => fn());
|
|
41
|
+
writeQueue = result.then(() => { }, () => { });
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
async function deriveKey(password, salt) {
|
|
45
|
+
const capped = password.slice(0, MAX_PASSWORD_LENGTH);
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
scrypt(capped, salt, KEY_LENGTH, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }, (err, key) => {
|
|
48
|
+
if (err)
|
|
49
|
+
reject(err);
|
|
50
|
+
else
|
|
51
|
+
resolve(key);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function encrypt(plaintext, password) {
|
|
56
|
+
const salt = randomBytes(SALT_LENGTH);
|
|
57
|
+
const iv = randomBytes(IV_LENGTH);
|
|
58
|
+
const key = await deriveKey(password, salt);
|
|
59
|
+
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
60
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf-8'), cipher.final()]);
|
|
61
|
+
const authTag = cipher.getAuthTag();
|
|
62
|
+
// Format: salt (32) + iv (16) + authTag (16) + ciphertext
|
|
63
|
+
return Buffer.concat([salt, iv, authTag, encrypted]);
|
|
64
|
+
}
|
|
65
|
+
async function decrypt(data, password) {
|
|
66
|
+
if (data.length < SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH + 1) {
|
|
67
|
+
throw new Error('Financial vault data is corrupted or empty');
|
|
68
|
+
}
|
|
69
|
+
const salt = data.subarray(0, SALT_LENGTH);
|
|
70
|
+
const iv = data.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
|
|
71
|
+
const authTag = data.subarray(SALT_LENGTH + IV_LENGTH, SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);
|
|
72
|
+
const ciphertext = data.subarray(SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);
|
|
73
|
+
const key = await deriveKey(password, salt);
|
|
74
|
+
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
75
|
+
decipher.setAuthTag(authTag);
|
|
76
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
77
|
+
return decrypted.toString('utf-8');
|
|
78
|
+
}
|
|
79
|
+
async function readVault(password) {
|
|
80
|
+
if (sessionCache) {
|
|
81
|
+
const a = Buffer.from(sessionCache.password, 'utf-8');
|
|
82
|
+
const b = Buffer.from(password, 'utf-8');
|
|
83
|
+
if (a.length === b.length && timingSafeEqual(a, b)) {
|
|
84
|
+
return { ...sessionCache.data }; // Return copy to prevent mutation
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!existsSync(VAULT_PATH)) {
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
const raw = await readFile(VAULT_PATH);
|
|
91
|
+
const json = await decrypt(raw, password);
|
|
92
|
+
const data = JSON.parse(json);
|
|
93
|
+
sessionCache = { password, data };
|
|
94
|
+
return { ...data };
|
|
95
|
+
}
|
|
96
|
+
async function writeVault(password, data) {
|
|
97
|
+
await mkdir(TREASURY_DIR, { recursive: true });
|
|
98
|
+
const json = JSON.stringify(data);
|
|
99
|
+
const encrypted = await encrypt(json, password);
|
|
100
|
+
// Atomic write: temp → fsync → rename (per ADR-1)
|
|
101
|
+
const tmpPath = VAULT_PATH + '.tmp.' + process.pid;
|
|
102
|
+
const fh = await open(tmpPath, 'w', 0o600);
|
|
103
|
+
try {
|
|
104
|
+
await fh.writeFile(encrypted);
|
|
105
|
+
// macOS: fsync() doesn't guarantee physical durability (§9.18)
|
|
106
|
+
// F_FULLFSYNC (fcntl flag 51) is required for financial files
|
|
107
|
+
// Node.js datasync is the closest built-in; document the gap
|
|
108
|
+
if (platform() === 'darwin') {
|
|
109
|
+
await fh.datasync();
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
await fh.sync();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
await fh.close();
|
|
117
|
+
}
|
|
118
|
+
await rename(tmpPath, VAULT_PATH);
|
|
119
|
+
sessionCache = { password, data: { ...data } };
|
|
120
|
+
}
|
|
121
|
+
// ── Public API ────────────────────────────────────────
|
|
122
|
+
const MIN_PASSWORD_LENGTH = 12;
|
|
123
|
+
function validatePassword(password) {
|
|
124
|
+
if (password.length < MIN_PASSWORD_LENGTH) {
|
|
125
|
+
throw new Error(`Financial vault password must be at least ${MIN_PASSWORD_LENGTH} characters`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** Store a financial credential */
|
|
129
|
+
export function financialVaultSet(password, key, value) {
|
|
130
|
+
validatePassword(password);
|
|
131
|
+
return serialized(async () => {
|
|
132
|
+
const data = await readVault(password);
|
|
133
|
+
data[key] = value;
|
|
134
|
+
await writeVault(password, data);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/** Retrieve a financial credential */
|
|
138
|
+
export function financialVaultGet(password, key) {
|
|
139
|
+
return serialized(async () => {
|
|
140
|
+
const data = await readVault(password);
|
|
141
|
+
return data[key] ?? null;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/** Delete a financial credential */
|
|
145
|
+
export function financialVaultDelete(password, key) {
|
|
146
|
+
return serialized(async () => {
|
|
147
|
+
const data = await readVault(password);
|
|
148
|
+
delete data[key];
|
|
149
|
+
await writeVault(password, data);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/** Check if the financial vault exists */
|
|
153
|
+
export function financialVaultExists() {
|
|
154
|
+
return existsSync(VAULT_PATH);
|
|
155
|
+
}
|
|
156
|
+
/** Verify password can decrypt the vault */
|
|
157
|
+
export function financialVaultUnlock(password) {
|
|
158
|
+
if (!existsSync(VAULT_PATH)) {
|
|
159
|
+
return Promise.resolve(true); // No vault yet = password is for creation
|
|
160
|
+
}
|
|
161
|
+
return serialized(async () => {
|
|
162
|
+
try {
|
|
163
|
+
await readVault(password);
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/** List stored keys (requires password) */
|
|
172
|
+
export function financialVaultKeys(password) {
|
|
173
|
+
return serialized(async () => {
|
|
174
|
+
const data = await readVault(password);
|
|
175
|
+
return Object.keys(data);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/** Zero the in-memory cache — call on SIGTERM, vault timeout, or manual lock */
|
|
179
|
+
export function financialVaultLock() {
|
|
180
|
+
if (sessionCache) {
|
|
181
|
+
// Best-effort memory zeroing — V8 doesn't guarantee this but it's defense in depth
|
|
182
|
+
sessionCache.password = '\0'.repeat(sessionCache.password.length);
|
|
183
|
+
sessionCache = null;
|
|
184
|
+
}
|
|
185
|
+
// VG-004: Also invalidate TOTP session when vault is locked
|
|
186
|
+
try {
|
|
187
|
+
// Dynamic import to avoid circular dependency — totp.ts is a sibling module
|
|
188
|
+
import('./totp.js').then(m => m.totpSessionInvalidate()).catch(() => { });
|
|
189
|
+
}
|
|
190
|
+
catch { /* totp module may not be loaded */ }
|
|
191
|
+
}
|
|
192
|
+
/** Return the vault file path */
|
|
193
|
+
export function financialVaultPath() {
|
|
194
|
+
return VAULT_PATH;
|
|
195
|
+
}
|
|
196
|
+
/** Return the treasury directory path */
|
|
197
|
+
export function treasuryDir() {
|
|
198
|
+
return TREASURY_DIR;
|
|
199
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal YAML frontmatter parser for PRD documents.
|
|
3
|
+
* Handles the simple key: value format used in PRD frontmatter blocks.
|
|
4
|
+
*/
|
|
5
|
+
export interface PrdFrontmatter {
|
|
6
|
+
name?: string;
|
|
7
|
+
type?: string;
|
|
8
|
+
framework?: string;
|
|
9
|
+
database?: string;
|
|
10
|
+
cache?: string;
|
|
11
|
+
styling?: string;
|
|
12
|
+
auth?: string;
|
|
13
|
+
payments?: string;
|
|
14
|
+
workers?: string;
|
|
15
|
+
admin?: string;
|
|
16
|
+
marketing?: string;
|
|
17
|
+
email?: string;
|
|
18
|
+
deploy?: string;
|
|
19
|
+
instance_type?: string;
|
|
20
|
+
hostname?: string;
|
|
21
|
+
language?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
[key: string]: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
export declare function parseFrontmatter(content: string): {
|
|
26
|
+
frontmatter: PrdFrontmatter;
|
|
27
|
+
body: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function generateFrontmatterBlock(fm: PrdFrontmatter): string;
|
|
30
|
+
export declare function validateFrontmatter(fm: PrdFrontmatter): string[];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal YAML frontmatter parser for PRD documents.
|
|
3
|
+
* Handles the simple key: value format used in PRD frontmatter blocks.
|
|
4
|
+
*/
|
|
5
|
+
export function parseFrontmatter(content) {
|
|
6
|
+
// Look for ```yaml ... ``` block in the frontmatter section
|
|
7
|
+
const yamlBlockMatch = content.match(/```yaml\s*\n([\s\S]*?)```/);
|
|
8
|
+
if (!yamlBlockMatch) {
|
|
9
|
+
return { frontmatter: {}, body: content };
|
|
10
|
+
}
|
|
11
|
+
const yamlStr = yamlBlockMatch[1];
|
|
12
|
+
const frontmatter = {};
|
|
13
|
+
for (const line of yamlStr.split('\n')) {
|
|
14
|
+
const trimmed = line.trim();
|
|
15
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
16
|
+
continue;
|
|
17
|
+
const match = trimmed.match(/^(\w+):\s*(.+)$/);
|
|
18
|
+
if (match) {
|
|
19
|
+
const key = match[1];
|
|
20
|
+
let value = match[2].trim();
|
|
21
|
+
// Track whether the original value was quoted
|
|
22
|
+
const wasQuoted = (value.startsWith('"') && value.endsWith('"')) ||
|
|
23
|
+
(value.startsWith("'") && value.endsWith("'"));
|
|
24
|
+
// Strip quotes
|
|
25
|
+
if (wasQuoted) {
|
|
26
|
+
value = value.slice(1, -1);
|
|
27
|
+
}
|
|
28
|
+
// Strip inline comments — but only if the value was NOT quoted
|
|
29
|
+
// (a `#` inside a quoted value is literal, not a comment)
|
|
30
|
+
if (!wasQuoted) {
|
|
31
|
+
const commentIdx = value.indexOf('#');
|
|
32
|
+
if (commentIdx > 0) {
|
|
33
|
+
value = value.slice(0, commentIdx).trim();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
frontmatter[key] = value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { frontmatter, body: content };
|
|
40
|
+
}
|
|
41
|
+
export function generateFrontmatterBlock(fm) {
|
|
42
|
+
const lines = ['```yaml'];
|
|
43
|
+
if (fm.name)
|
|
44
|
+
lines.push(`name: "${fm.name}"`);
|
|
45
|
+
if (fm.type)
|
|
46
|
+
lines.push(`type: "${fm.type}"`);
|
|
47
|
+
lines.push('');
|
|
48
|
+
if (fm.framework)
|
|
49
|
+
lines.push(`framework: "${fm.framework}"`);
|
|
50
|
+
if (fm.database)
|
|
51
|
+
lines.push(`database: "${fm.database}"`);
|
|
52
|
+
if (fm.cache)
|
|
53
|
+
lines.push(`cache: "${fm.cache}"`);
|
|
54
|
+
if (fm.styling)
|
|
55
|
+
lines.push(`styling: "${fm.styling}"`);
|
|
56
|
+
lines.push('');
|
|
57
|
+
if (fm.auth)
|
|
58
|
+
lines.push(`auth: ${fm.auth}`);
|
|
59
|
+
if (fm.payments)
|
|
60
|
+
lines.push(`payments: ${fm.payments}`);
|
|
61
|
+
if (fm.workers)
|
|
62
|
+
lines.push(`workers: ${fm.workers}`);
|
|
63
|
+
if (fm.admin)
|
|
64
|
+
lines.push(`admin: ${fm.admin}`);
|
|
65
|
+
if (fm.marketing)
|
|
66
|
+
lines.push(`marketing: ${fm.marketing}`);
|
|
67
|
+
if (fm.email)
|
|
68
|
+
lines.push(`email: ${fm.email}`);
|
|
69
|
+
lines.push('');
|
|
70
|
+
if (fm.deploy)
|
|
71
|
+
lines.push(`deploy: "${fm.deploy}"`);
|
|
72
|
+
if (fm.instance_type)
|
|
73
|
+
lines.push(`instance_type: "${fm.instance_type}"`);
|
|
74
|
+
if (fm.hostname)
|
|
75
|
+
lines.push(`hostname: "${fm.hostname}"`);
|
|
76
|
+
lines.push('```');
|
|
77
|
+
return lines.join('\n');
|
|
78
|
+
}
|
|
79
|
+
const VALID_TYPES = ['full-stack', 'api-only', 'static-site', 'prototype'];
|
|
80
|
+
const VALID_DEPLOY = ['vps', 'vercel', 'railway', 'cloudflare', 'static', 'docker'];
|
|
81
|
+
const VALID_INSTANCE_TYPES = ['t3.micro', 't3.small', 't3.medium', 't3.large'];
|
|
82
|
+
export function validateFrontmatter(fm) {
|
|
83
|
+
const errors = [];
|
|
84
|
+
if (!fm.name)
|
|
85
|
+
errors.push('Missing required field: name');
|
|
86
|
+
if (fm.type && !VALID_TYPES.includes(fm.type)) {
|
|
87
|
+
errors.push(`Invalid type: "${fm.type}". Must be one of: ${VALID_TYPES.join(', ')}`);
|
|
88
|
+
}
|
|
89
|
+
if (fm.deploy && !VALID_DEPLOY.includes(fm.deploy)) {
|
|
90
|
+
errors.push(`Invalid deploy: "${fm.deploy}". Must be one of: ${VALID_DEPLOY.join(', ')}`);
|
|
91
|
+
}
|
|
92
|
+
if (fm.instance_type && !VALID_INSTANCE_TYPES.includes(fm.instance_type)) {
|
|
93
|
+
errors.push(`Invalid instance_type: "${fm.instance_type}". Must be one of: ${VALID_INSTANCE_TYPES.join(', ')}`);
|
|
94
|
+
}
|
|
95
|
+
return errors;
|
|
96
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seven's Gap Analysis Engine — 5-dimension project scoring (v12.1).
|
|
3
|
+
*
|
|
4
|
+
* Analyzes a project across: feature completeness, quality, performance,
|
|
5
|
+
* growth readiness, and revenue potential. Produces a scored situation model
|
|
6
|
+
* that drives Tuvok's campaign proposals.
|
|
7
|
+
*
|
|
8
|
+
* PRD Reference: ROADMAP v12.1 deliverables, DEEP_CURRENT.md ANALYZE step
|
|
9
|
+
*/
|
|
10
|
+
import type { SituationModel, DimensionScore } from './deep-current.js';
|
|
11
|
+
/**
|
|
12
|
+
* Score feature completeness by diffing PRD requirements against codebase.
|
|
13
|
+
* Uses a simplified heuristic: count PRD sections vs implemented routes/components.
|
|
14
|
+
*/
|
|
15
|
+
export declare function scoreFeatureCompleteness(projectDir: string): Promise<DimensionScore>;
|
|
16
|
+
/**
|
|
17
|
+
* Score quality from gauntlet findings, field reports, and test presence.
|
|
18
|
+
*/
|
|
19
|
+
export declare function scoreQuality(projectDir: string): Promise<DimensionScore>;
|
|
20
|
+
/**
|
|
21
|
+
* Score revenue potential from treasury setup, payment integrations, and pricing presence.
|
|
22
|
+
*/
|
|
23
|
+
export declare function scoreRevenuePotential(projectDir: string): Promise<DimensionScore>;
|
|
24
|
+
/**
|
|
25
|
+
* Run Seven's full gap analysis across all 5 dimensions.
|
|
26
|
+
* Performance and growthReadiness come from Torres's site scan (already in the model).
|
|
27
|
+
* Feature completeness, quality, and revenue potential are analyzed here.
|
|
28
|
+
*/
|
|
29
|
+
export declare function analyzeGaps(model: SituationModel, projectDir: string): Promise<SituationModel>;
|
|
30
|
+
/**
|
|
31
|
+
* Find the weakest dimension — this drives the next campaign proposal.
|
|
32
|
+
*/
|
|
33
|
+
export declare function findWeakestDimension(model: SituationModel): {
|
|
34
|
+
name: string;
|
|
35
|
+
score: number;
|
|
36
|
+
gaps: string[];
|
|
37
|
+
};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seven's Gap Analysis Engine — 5-dimension project scoring (v12.1).
|
|
3
|
+
*
|
|
4
|
+
* Analyzes a project across: feature completeness, quality, performance,
|
|
5
|
+
* growth readiness, and revenue potential. Produces a scored situation model
|
|
6
|
+
* that drives Tuvok's campaign proposals.
|
|
7
|
+
*
|
|
8
|
+
* PRD Reference: ROADMAP v12.1 deliverables, DEEP_CURRENT.md ANALYZE step
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { homedir } from 'node:os';
|
|
14
|
+
// ── Feature Completeness Dimension ────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Score feature completeness by diffing PRD requirements against codebase.
|
|
17
|
+
* Uses a simplified heuristic: count PRD sections vs implemented routes/components.
|
|
18
|
+
*/
|
|
19
|
+
export async function scoreFeatureCompleteness(projectDir) {
|
|
20
|
+
const gaps = [];
|
|
21
|
+
let score = 50; // base — assumes halfway done if we can't fully analyze
|
|
22
|
+
// Read PRD if exists
|
|
23
|
+
const prdPaths = [join(projectDir, 'docs', 'PRD.md'), join(projectDir, 'PRD.md')];
|
|
24
|
+
let prdContent = '';
|
|
25
|
+
for (const p of prdPaths) {
|
|
26
|
+
if (existsSync(p)) {
|
|
27
|
+
prdContent = await readFile(p, 'utf-8');
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (!prdContent) {
|
|
32
|
+
return { score: 0, gaps: ['No PRD found — cannot assess feature completeness'], lastUpdated: new Date().toISOString() };
|
|
33
|
+
}
|
|
34
|
+
// Count PRD feature sections (## headers that look like features)
|
|
35
|
+
const featureSections = (prdContent.match(/^#{2,3}\s+(?!Frontmatter|Version|Status)/gm) || []).length;
|
|
36
|
+
// Count source files as a proxy for implementation
|
|
37
|
+
let sourceFileCount = 0;
|
|
38
|
+
try {
|
|
39
|
+
const srcDirs = ['src', 'app', 'pages', 'components', 'lib', 'services', 'wizard/lib', 'wizard/ui'];
|
|
40
|
+
for (const dir of srcDirs) {
|
|
41
|
+
const fullDir = join(projectDir, dir);
|
|
42
|
+
if (existsSync(fullDir)) {
|
|
43
|
+
const files = await readdir(fullDir, { recursive: true });
|
|
44
|
+
sourceFileCount += files.filter(f => /\.(ts|tsx|js|jsx|py|rb)$/.test(String(f))).length;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch { /* can't read dirs */ }
|
|
49
|
+
// Heuristic scoring
|
|
50
|
+
if (featureSections > 0 && sourceFileCount > 0) {
|
|
51
|
+
const ratio = Math.min(sourceFileCount / featureSections, 3); // cap at 3 files/section
|
|
52
|
+
score = Math.min(100, Math.round(ratio / 3 * 100));
|
|
53
|
+
}
|
|
54
|
+
if (score < 30)
|
|
55
|
+
gaps.push(`Only ${sourceFileCount} source files for ${featureSections} PRD sections`);
|
|
56
|
+
if (score < 60)
|
|
57
|
+
gaps.push('Feature implementation appears incomplete');
|
|
58
|
+
// Check for campaign state — completed campaigns boost the score
|
|
59
|
+
const campaignStatePath = join(projectDir, 'logs', 'campaign-state.md');
|
|
60
|
+
if (existsSync(campaignStatePath)) {
|
|
61
|
+
const campaignContent = await readFile(campaignStatePath, 'utf-8');
|
|
62
|
+
const completedMissions = (campaignContent.match(/COMPLETE/g) || []).length;
|
|
63
|
+
score = Math.min(100, score + completedMissions * 3);
|
|
64
|
+
}
|
|
65
|
+
return { score: Math.min(100, score), gaps, lastUpdated: new Date().toISOString() };
|
|
66
|
+
}
|
|
67
|
+
// ── Quality Dimension ─────────────────────────────────
|
|
68
|
+
/**
|
|
69
|
+
* Score quality from gauntlet findings, field reports, and test presence.
|
|
70
|
+
*/
|
|
71
|
+
export async function scoreQuality(projectDir) {
|
|
72
|
+
const gaps = [];
|
|
73
|
+
let score = 50;
|
|
74
|
+
// Check for test files
|
|
75
|
+
let testFileCount = 0;
|
|
76
|
+
const testDirs = ['__tests__', 'tests', 'test', 'spec'];
|
|
77
|
+
for (const dir of testDirs) {
|
|
78
|
+
const fullDir = join(projectDir, dir);
|
|
79
|
+
if (existsSync(fullDir)) {
|
|
80
|
+
const files = await readdir(fullDir, { recursive: true });
|
|
81
|
+
testFileCount += files.filter(f => /\.(test|spec)\.(ts|tsx|js|jsx|py)$/.test(String(f))).length;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Also check for test files alongside source
|
|
85
|
+
try {
|
|
86
|
+
const srcDir = join(projectDir, 'src');
|
|
87
|
+
if (existsSync(srcDir)) {
|
|
88
|
+
const files = await readdir(srcDir, { recursive: true });
|
|
89
|
+
testFileCount += files.filter(f => /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(String(f))).length;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch { /* can't read */ }
|
|
93
|
+
if (testFileCount === 0) {
|
|
94
|
+
gaps.push('No test files found');
|
|
95
|
+
score -= 20;
|
|
96
|
+
}
|
|
97
|
+
else if (testFileCount < 5) {
|
|
98
|
+
gaps.push(`Only ${testFileCount} test files — coverage likely low`);
|
|
99
|
+
score -= 10;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
score += 15;
|
|
103
|
+
}
|
|
104
|
+
// Check gauntlet state for unfixed findings
|
|
105
|
+
const gauntletPath = join(projectDir, 'logs', 'gauntlet-state.md');
|
|
106
|
+
if (existsSync(gauntletPath)) {
|
|
107
|
+
const content = await readFile(gauntletPath, 'utf-8');
|
|
108
|
+
if (content.includes('COMPLETE') && content.includes('SIGN OFF')) {
|
|
109
|
+
score += 20;
|
|
110
|
+
}
|
|
111
|
+
const criticalCount = (content.match(/Critical/gi) || []).length;
|
|
112
|
+
if (criticalCount > 5) {
|
|
113
|
+
gaps.push(`${criticalCount} Critical mentions in gauntlet state`);
|
|
114
|
+
score -= 10;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
gaps.push('No gauntlet history — project has not been reviewed');
|
|
119
|
+
score -= 15;
|
|
120
|
+
}
|
|
121
|
+
// Check for LESSONS.md (learning from mistakes = quality signal)
|
|
122
|
+
if (existsSync(join(projectDir, 'docs', 'LESSONS.md')))
|
|
123
|
+
score += 5;
|
|
124
|
+
return { score: Math.max(0, Math.min(100, score)), gaps, lastUpdated: new Date().toISOString() };
|
|
125
|
+
}
|
|
126
|
+
// ── Revenue Potential Dimension ───────────────────────
|
|
127
|
+
/**
|
|
128
|
+
* Score revenue potential from treasury setup, payment integrations, and pricing presence.
|
|
129
|
+
*/
|
|
130
|
+
export async function scoreRevenuePotential(projectDir) {
|
|
131
|
+
const gaps = [];
|
|
132
|
+
let score = 20; // base — most projects start without revenue infrastructure
|
|
133
|
+
// Check for treasury vault (Cultivation installed)
|
|
134
|
+
const treasuryVault = join(homedir(), '.voidforge', 'treasury', 'vault.enc');
|
|
135
|
+
if (existsSync(treasuryVault)) {
|
|
136
|
+
score += 25;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
gaps.push('No treasury set up — no revenue tracking');
|
|
140
|
+
}
|
|
141
|
+
// Check for payment integration in code
|
|
142
|
+
const paymentIndicators = ['stripe', 'paddle', 'lemon', 'paypal', 'checkout'];
|
|
143
|
+
let paymentFound = false;
|
|
144
|
+
try {
|
|
145
|
+
const envPath = join(projectDir, '.env');
|
|
146
|
+
if (existsSync(envPath)) {
|
|
147
|
+
const envContent = await readFile(envPath, 'utf-8');
|
|
148
|
+
for (const indicator of paymentIndicators) {
|
|
149
|
+
if (envContent.toLowerCase().includes(indicator)) {
|
|
150
|
+
paymentFound = true;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch { /* can't read .env */ }
|
|
157
|
+
if (paymentFound) {
|
|
158
|
+
score += 20;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
gaps.push('No payment integration detected');
|
|
162
|
+
}
|
|
163
|
+
// Check for pricing page indicators in source
|
|
164
|
+
try {
|
|
165
|
+
const srcDir = join(projectDir, 'src');
|
|
166
|
+
if (existsSync(srcDir)) {
|
|
167
|
+
const files = await readdir(srcDir, { recursive: true });
|
|
168
|
+
const pricingFiles = files.filter(f => /pric/i.test(String(f)));
|
|
169
|
+
if (pricingFiles.length > 0) {
|
|
170
|
+
score += 15;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
gaps.push('No pricing page or component found');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch { /* can't read */ }
|
|
178
|
+
// Check for heartbeat daemon (revenue monitoring)
|
|
179
|
+
if (existsSync(join(homedir(), '.voidforge', 'heartbeat.json'))) {
|
|
180
|
+
score += 15;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
gaps.push('Heartbeat daemon not running — no revenue monitoring');
|
|
184
|
+
}
|
|
185
|
+
return { score: Math.max(0, Math.min(100, score)), gaps, lastUpdated: new Date().toISOString() };
|
|
186
|
+
}
|
|
187
|
+
// ── Full Analysis ─────────────────────────────────────
|
|
188
|
+
/**
|
|
189
|
+
* Run Seven's full gap analysis across all 5 dimensions.
|
|
190
|
+
* Performance and growthReadiness come from Torres's site scan (already in the model).
|
|
191
|
+
* Feature completeness, quality, and revenue potential are analyzed here.
|
|
192
|
+
*/
|
|
193
|
+
export async function analyzeGaps(model, projectDir) {
|
|
194
|
+
// Feature completeness
|
|
195
|
+
model.dimensions.featureCompleteness = await scoreFeatureCompleteness(projectDir);
|
|
196
|
+
// Quality
|
|
197
|
+
model.dimensions.quality = await scoreQuality(projectDir);
|
|
198
|
+
// Revenue potential
|
|
199
|
+
model.dimensions.revenuePotential = await scoreRevenuePotential(projectDir);
|
|
200
|
+
// Performance and growthReadiness are updated by Torres's SENSE step (site scanner)
|
|
201
|
+
// If they haven't been scanned yet, keep their current values
|
|
202
|
+
return model;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Find the weakest dimension — this drives the next campaign proposal.
|
|
206
|
+
*/
|
|
207
|
+
export function findWeakestDimension(model) {
|
|
208
|
+
const dims = model.dimensions;
|
|
209
|
+
const entries = [
|
|
210
|
+
{ name: 'Feature Completeness', ...dims.featureCompleteness },
|
|
211
|
+
{ name: 'Quality', ...dims.quality },
|
|
212
|
+
{ name: 'Performance', ...dims.performance },
|
|
213
|
+
{ name: 'Growth Readiness', ...dims.growthReadiness },
|
|
214
|
+
{ name: 'Revenue Potential', ...dims.revenuePotential },
|
|
215
|
+
];
|
|
216
|
+
entries.sort((a, b) => a.score - b.score);
|
|
217
|
+
return entries[0];
|
|
218
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub integration — create repo, git init + remote + push.
|
|
3
|
+
* Runs as a pre-provision step (ADR-011) so platforms can link to the repo.
|
|
4
|
+
* Uses exec.ts for git operations (ADR-013). No npm dependencies.
|
|
5
|
+
*/
|
|
6
|
+
import type { ProvisionEmitter } from './provisioners/types.js';
|
|
7
|
+
export interface GitHubResult {
|
|
8
|
+
success: boolean;
|
|
9
|
+
repoUrl?: string;
|
|
10
|
+
owner?: string;
|
|
11
|
+
repoName?: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Full GitHub pre-provision flow:
|
|
16
|
+
* 1. Validate git binary exists
|
|
17
|
+
* 2. Determine owner (from vault or token)
|
|
18
|
+
* 3. Create repo via API (or use existing)
|
|
19
|
+
* 4. Ensure .gitignore has .ssh/ and .env
|
|
20
|
+
* 5. git init + remote + push
|
|
21
|
+
*/
|
|
22
|
+
export declare function prepareGithub(runId: string, token: string, owner: string | null, projectName: string, projectDir: string, emit: ProvisionEmitter, abortSignal?: AbortSignal, framework?: string, deployTarget?: string): Promise<GitHubResult>;
|