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,436 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { readFile, stat, readdir } from 'node:fs/promises';
|
|
3
|
+
import { join, extname, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { route, addRoute } from './router.js';
|
|
6
|
+
// Node 20 LTS compatibility — import.meta.dirname requires Node 21.2+ (field report #122)
|
|
7
|
+
// @ts-ignore — polyfill for environments where import.meta.dirname is undefined
|
|
8
|
+
if (!import.meta.dirname)
|
|
9
|
+
import.meta.dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
10
|
+
import './api/credentials.js';
|
|
11
|
+
import './api/cloud-providers.js';
|
|
12
|
+
import './api/prd.js';
|
|
13
|
+
import './api/project.js';
|
|
14
|
+
import './api/provision.js';
|
|
15
|
+
import './api/deploy.js';
|
|
16
|
+
// Lazy terminal import — node-pty is a native C++ module that may not be installed (field report #122)
|
|
17
|
+
try {
|
|
18
|
+
await import('./api/terminal.js');
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
console.warn('Terminal module not available (node-pty not installed). PTY features disabled.');
|
|
22
|
+
}
|
|
23
|
+
import './api/projects.js';
|
|
24
|
+
import './api/auth.js';
|
|
25
|
+
import './api/users.js';
|
|
26
|
+
import './api/blueprint.js';
|
|
27
|
+
import './api/danger-room.js';
|
|
28
|
+
import './api/war-room.js';
|
|
29
|
+
// v17.0: Register server status via addRoute for auth middleware coverage in remote mode.
|
|
30
|
+
// Previously this was a hardcoded path handler that bypassed auth.
|
|
31
|
+
addRoute('GET', '/api/server/status', async (_req, res) => {
|
|
32
|
+
const needsRestart = await checkNativeModulesChanged();
|
|
33
|
+
sendJson(res, 200, { needsRestart });
|
|
34
|
+
});
|
|
35
|
+
// Lazy import — may not exist if node-pty is not installed
|
|
36
|
+
let handleTerminalUpgrade = null;
|
|
37
|
+
try {
|
|
38
|
+
handleTerminalUpgrade = (await import('./api/terminal.js')).handleTerminalUpgrade;
|
|
39
|
+
}
|
|
40
|
+
catch { /* node-pty not available */ }
|
|
41
|
+
import { handleDangerRoomUpgrade, closeDangerRoom } from './api/danger-room.js';
|
|
42
|
+
import { handleWarRoomUpgrade, closeWarRoom } from './api/war-room.js';
|
|
43
|
+
let killAllSessions = null;
|
|
44
|
+
try {
|
|
45
|
+
killAllSessions = (await import('./lib/pty-manager.js')).killAllSessions;
|
|
46
|
+
}
|
|
47
|
+
catch { /* node-pty not available */ }
|
|
48
|
+
import { startHealthPoller, stopHealthPoller } from './lib/health-poller.js';
|
|
49
|
+
import { isPrivateOrigin } from './lib/network.js';
|
|
50
|
+
import { isRemoteMode, setRemoteMode, isLanMode, setLanMode, validateSession, parseSessionCookie, isAuthExempt, getClientIp } from './lib/tower-auth.js';
|
|
51
|
+
import { initAuditLog, audit } from './lib/audit-log.js';
|
|
52
|
+
import { hasRole } from './lib/user-manager.js';
|
|
53
|
+
// ── Route-level RBAC ────────────────────────────────
|
|
54
|
+
// Maps API path prefixes to minimum required role.
|
|
55
|
+
// Routes not listed here are accessible to any authenticated user.
|
|
56
|
+
// Auth-exempt routes bypass this entirely (handled by isAuthExempt).
|
|
57
|
+
const ROUTE_ROLES = [
|
|
58
|
+
// Admin-only: user management
|
|
59
|
+
{ prefix: '/api/users/invite', minRole: 'admin' },
|
|
60
|
+
{ prefix: '/api/users/remove', minRole: 'admin' },
|
|
61
|
+
{ prefix: '/api/users/role', minRole: 'admin' },
|
|
62
|
+
{ prefix: '/api/users', minRole: 'admin' }, // GET /api/users (list)
|
|
63
|
+
// Deployer+: infrastructure, credentials, terminals, project mutations
|
|
64
|
+
{ prefix: '/api/credentials', minRole: 'deployer' },
|
|
65
|
+
{ prefix: '/api/provision', minRole: 'deployer' },
|
|
66
|
+
{ prefix: '/api/deploy', minRole: 'deployer' },
|
|
67
|
+
{ prefix: '/api/terminal', minRole: 'deployer' },
|
|
68
|
+
{ prefix: '/api/project/create', minRole: 'deployer' },
|
|
69
|
+
{ prefix: '/api/projects/access', minRole: 'deployer' }, // Fine-grained owner check in handler
|
|
70
|
+
{ prefix: '/api/projects/link', minRole: 'deployer' }, // Owner check in handler
|
|
71
|
+
{ prefix: '/api/projects/unlink', minRole: 'deployer' }, // Owner check in handler
|
|
72
|
+
{ prefix: '/api/projects/deploy-check', minRole: 'deployer' },
|
|
73
|
+
{ prefix: '/api/projects/import', minRole: 'deployer' },
|
|
74
|
+
{ prefix: '/api/projects/delete', minRole: 'deployer' },
|
|
75
|
+
{ prefix: '/api/prd', minRole: 'deployer' },
|
|
76
|
+
{ prefix: '/api/cloud', minRole: 'deployer' },
|
|
77
|
+
{ prefix: '/api/deploys', minRole: 'deployer' },
|
|
78
|
+
{ prefix: '/api/project/validate', minRole: 'deployer' },
|
|
79
|
+
{ prefix: '/api/project/defaults', minRole: 'deployer' },
|
|
80
|
+
// Deployer+: freeze spending (safety-critical operation)
|
|
81
|
+
{ prefix: '/api/danger-room/freeze', minRole: 'deployer' },
|
|
82
|
+
// Viewer: read-only endpoints (GET /api/projects, GET /api/auth/session, /api/danger-room/*) — no entry needed
|
|
83
|
+
];
|
|
84
|
+
function getMinRole(pathname) {
|
|
85
|
+
for (const rule of ROUTE_ROLES) {
|
|
86
|
+
if (pathname === rule.prefix || pathname.startsWith(rule.prefix + '/')) {
|
|
87
|
+
return rule.minRole;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null; // No restriction — any authenticated user
|
|
91
|
+
}
|
|
92
|
+
const UI_DIR = join(import.meta.dirname, 'ui');
|
|
93
|
+
/** Set by startServer so handleRequest can scope CORS to the actual origin. */
|
|
94
|
+
// Server config shared via wizard/lib/server-config.ts (breaks circular import — Gauntlet DR-02)
|
|
95
|
+
import { getServerPort, getServerHost, setServerPort, setServerHost } from './lib/server-config.js';
|
|
96
|
+
const MIME_TYPES = {
|
|
97
|
+
'.html': 'text/html; charset=utf-8',
|
|
98
|
+
'.css': 'text/css; charset=utf-8',
|
|
99
|
+
'.js': 'application/javascript; charset=utf-8',
|
|
100
|
+
'.json': 'application/json; charset=utf-8',
|
|
101
|
+
'.svg': 'image/svg+xml',
|
|
102
|
+
'.png': 'image/png',
|
|
103
|
+
'.ico': 'image/x-icon',
|
|
104
|
+
};
|
|
105
|
+
// Use shared sendJson from http-helpers.ts — do NOT redefine here (Gauntlet DR-07)
|
|
106
|
+
import { sendJson } from './lib/http-helpers.js';
|
|
107
|
+
async function serveStatic(res, filePath) {
|
|
108
|
+
try {
|
|
109
|
+
const content = await readFile(filePath);
|
|
110
|
+
const ext = extname(filePath);
|
|
111
|
+
const mime = MIME_TYPES[ext] ?? 'application/octet-stream';
|
|
112
|
+
res.writeHead(200, {
|
|
113
|
+
'Content-Type': mime,
|
|
114
|
+
'Cache-Control': 'no-cache, must-revalidate',
|
|
115
|
+
});
|
|
116
|
+
res.end(content);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
120
|
+
res.end('Not Found');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function handleRequest(req, res) {
|
|
124
|
+
// CORS — scoped to the wizard's own origin (expanded for remote mode)
|
|
125
|
+
const origin = req.headers.origin ?? '';
|
|
126
|
+
const allowedOrigins = [`http://127.0.0.1:${getServerPort()}`, `http://localhost:${getServerPort()}`];
|
|
127
|
+
if (isRemoteMode() && getServerHost()) {
|
|
128
|
+
allowedOrigins.push(`https://${getServerHost()}`);
|
|
129
|
+
}
|
|
130
|
+
// LAN mode: accept any private IP origin (Gauntlet Picard DR-04)
|
|
131
|
+
if (allowedOrigins.includes(origin) || (isLanMode() && isPrivateOrigin(origin))) {
|
|
132
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
133
|
+
}
|
|
134
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
135
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-VoidForge-Request');
|
|
136
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
137
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
138
|
+
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
139
|
+
res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
|
|
140
|
+
// SEC-R2-003: HSTS only when actually serving HTTPS — not on plain HTTP
|
|
141
|
+
// (browsers should ignore HSTS over HTTP, but sending it is misleading)
|
|
142
|
+
if (req.headers['x-forwarded-proto'] === 'https' || req.socket.encrypted) {
|
|
143
|
+
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
144
|
+
}
|
|
145
|
+
// LAN mode: allow WebSocket from any private IP (Gauntlet Kenobi DR-10)
|
|
146
|
+
const connectSrc = isRemoteMode() && getServerHost()
|
|
147
|
+
? `'self' ws://localhost:${getServerPort()} ws://127.0.0.1:${getServerPort()} wss://${getServerHost()}`
|
|
148
|
+
: isLanMode()
|
|
149
|
+
? `'self' ws://localhost:${getServerPort()} ws://127.0.0.1:${getServerPort()} ws://*:${getServerPort()}`
|
|
150
|
+
: `'self' ws://localhost:${getServerPort()} ws://127.0.0.1:${getServerPort()}`;
|
|
151
|
+
res.setHeader('Content-Security-Policy', `default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; img-src 'self' data:; connect-src ${connectSrc} https://cdn.jsdelivr.net; frame-ancestors 'none'`);
|
|
152
|
+
if (req.method === 'OPTIONS') {
|
|
153
|
+
res.writeHead(204);
|
|
154
|
+
res.end();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
// CSRF protection — require custom header on all POST requests (F-06)
|
|
158
|
+
if (req.method === 'POST' && !req.headers['x-voidforge-request']) {
|
|
159
|
+
sendJson(res, 403, { success: false, error: 'Missing X-VoidForge-Request header' });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
// LAN mode — restrict to dashboard-only endpoints (read-only, no credentials/deploy/terminal)
|
|
163
|
+
if (isLanMode() && !isRemoteMode()) {
|
|
164
|
+
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
|
165
|
+
const path = url.pathname;
|
|
166
|
+
const isLanSafe = path.startsWith('/api/danger-room/')
|
|
167
|
+
|| path.startsWith('/api/war-room/')
|
|
168
|
+
|| path === '/api/danger-room/heartbeat'
|
|
169
|
+
|| path.endsWith('.html') || path.endsWith('.js') || path.endsWith('.css')
|
|
170
|
+
|| path.endsWith('.svg') || path.endsWith('.png') || path.endsWith('.ico')
|
|
171
|
+
|| path === '/' || path === '/danger-room.html' || path === '/war-room.html'
|
|
172
|
+
|| path === '/lobby.html' || path.startsWith('/styles');
|
|
173
|
+
if (!isLanSafe) {
|
|
174
|
+
sendJson(res, 403, { success: false, error: 'Endpoint not available in LAN mode. Use --remote for full access.' });
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Auth middleware — in remote mode, require valid session for non-exempt paths
|
|
179
|
+
if (isRemoteMode()) {
|
|
180
|
+
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
|
181
|
+
if (!isAuthExempt(url.pathname)) {
|
|
182
|
+
const token = parseSessionCookie(req.headers.cookie);
|
|
183
|
+
const ip = getClientIp(req);
|
|
184
|
+
const session = token ? validateSession(token, ip) : null;
|
|
185
|
+
if (!session) {
|
|
186
|
+
// API requests get 401, page requests get redirected to login
|
|
187
|
+
if (url.pathname.startsWith('/api/')) {
|
|
188
|
+
sendJson(res, 401, { success: false, error: 'Authentication required' });
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
res.writeHead(302, { Location: '/login.html' });
|
|
192
|
+
res.end();
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
// RBAC — check route-level minimum role
|
|
197
|
+
const minRole = getMinRole(url.pathname);
|
|
198
|
+
if (minRole && !hasRole(session, minRole)) {
|
|
199
|
+
await audit('access_denied', ip, session.username, {
|
|
200
|
+
path: url.pathname,
|
|
201
|
+
role: session.role,
|
|
202
|
+
required: minRole,
|
|
203
|
+
});
|
|
204
|
+
// Return 404 not 403 — no information leakage (per CLAUDE.md)
|
|
205
|
+
sendJson(res, 404, { success: false, error: 'Not found' });
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Try API routes first
|
|
211
|
+
const handler = route(req, res);
|
|
212
|
+
if (handler) {
|
|
213
|
+
try {
|
|
214
|
+
await handler(req, res);
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
const message = err instanceof Error ? err.message : 'Internal server error';
|
|
218
|
+
console.error('API error:', message);
|
|
219
|
+
sendJson(res, 500, { success: false, error: 'Internal server error' });
|
|
220
|
+
}
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// v17.0: /api/server/status moved to addRoute() registration below for auth middleware coverage.
|
|
224
|
+
const reqUrl = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
|
225
|
+
// Static file serving
|
|
226
|
+
const url = reqUrl;
|
|
227
|
+
let pathname = url.pathname;
|
|
228
|
+
if (pathname === '/' || pathname === '') {
|
|
229
|
+
pathname = '/lobby.html';
|
|
230
|
+
}
|
|
231
|
+
// Prevent directory traversal — resolve then verify prefix
|
|
232
|
+
const safePath = resolve(UI_DIR, '.' + pathname);
|
|
233
|
+
if (!safePath.startsWith(UI_DIR)) {
|
|
234
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
235
|
+
res.end('Not Found');
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
await serveStatic(res, safePath);
|
|
239
|
+
}
|
|
240
|
+
// ── Native module mtime detection (tech debt #11) ──
|
|
241
|
+
// At startup, snapshot the mtimes of all .node files. On each Lobby request,
|
|
242
|
+
// compare. If any changed, the server is running stale native modules.
|
|
243
|
+
let nativeModuleMtimes = new Map();
|
|
244
|
+
async function findNodeFiles(dir) {
|
|
245
|
+
const results = [];
|
|
246
|
+
try {
|
|
247
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
248
|
+
for (const entry of entries) {
|
|
249
|
+
const fullPath = join(dir, entry.name);
|
|
250
|
+
if (entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
251
|
+
results.push(...await findNodeFiles(fullPath));
|
|
252
|
+
}
|
|
253
|
+
else if (entry.name.endsWith('.node')) {
|
|
254
|
+
results.push(fullPath);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch { /* skip unreadable directories */ }
|
|
259
|
+
return results;
|
|
260
|
+
}
|
|
261
|
+
async function snapshotNativeModules() {
|
|
262
|
+
try {
|
|
263
|
+
const nodeModulesDir = resolve(join(import.meta.dirname, '..', 'node_modules'));
|
|
264
|
+
const files = await findNodeFiles(nodeModulesDir);
|
|
265
|
+
for (const fullPath of files) {
|
|
266
|
+
const s = await stat(fullPath);
|
|
267
|
+
nativeModuleMtimes.set(fullPath, s.mtimeMs);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch { /* non-fatal — if scan fails, we just can't detect changes */ }
|
|
271
|
+
}
|
|
272
|
+
export async function checkNativeModulesChanged() {
|
|
273
|
+
try {
|
|
274
|
+
for (const [path, mtime] of nativeModuleMtimes) {
|
|
275
|
+
const s = await stat(path);
|
|
276
|
+
if (s.mtimeMs !== mtime)
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch { /* file missing = changed */
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
// Module-level reference to IPv6 loopback proxy so shutdown() can close it
|
|
286
|
+
let ipv6Proxy = null;
|
|
287
|
+
export function startServer(port, options) {
|
|
288
|
+
setServerPort(port);
|
|
289
|
+
if (options?.remote) {
|
|
290
|
+
setRemoteMode(true);
|
|
291
|
+
setServerHost(options.host ?? '');
|
|
292
|
+
}
|
|
293
|
+
if (options?.lan) {
|
|
294
|
+
setLanMode(true);
|
|
295
|
+
}
|
|
296
|
+
return new Promise((resolve, reject) => {
|
|
297
|
+
const server = createServer((req, res) => {
|
|
298
|
+
handleRequest(req, res).catch((err) => {
|
|
299
|
+
console.error('Unhandled error:', err);
|
|
300
|
+
if (!res.headersSent) {
|
|
301
|
+
res.writeHead(500);
|
|
302
|
+
res.end('Internal Server Error');
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
server.on('error', (err) => {
|
|
307
|
+
if (err.code === 'EADDRINUSE') {
|
|
308
|
+
console.error(`Port ${port} is in use. Set VOIDFORGE_PORT to use a different port.`);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
reject(err);
|
|
312
|
+
});
|
|
313
|
+
// WebSocket upgrade handler for terminal and Danger Room connections
|
|
314
|
+
server.on('upgrade', (req, socket, head) => {
|
|
315
|
+
console.error('[UPGRADE]', req.url, 'from', req.socket.remoteAddress);
|
|
316
|
+
const url = new URL(req.url || '', `http://localhost:${port}`);
|
|
317
|
+
if (url.pathname === '/ws/terminal') {
|
|
318
|
+
// Terminal: deployer minimum in remote mode
|
|
319
|
+
let wsSession;
|
|
320
|
+
if (isRemoteMode()) {
|
|
321
|
+
const token = parseSessionCookie(req.headers.cookie);
|
|
322
|
+
const ip = getClientIp(req);
|
|
323
|
+
const session = token ? validateSession(token, ip) : null;
|
|
324
|
+
if (!session) {
|
|
325
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
326
|
+
socket.destroy();
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (!hasRole(session, 'deployer')) {
|
|
330
|
+
socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
|
|
331
|
+
socket.destroy();
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
wsSession = session;
|
|
335
|
+
}
|
|
336
|
+
if (!handleTerminalUpgrade) {
|
|
337
|
+
socket.write('HTTP/1.1 503 Service Unavailable\r\n\r\n');
|
|
338
|
+
socket.destroy();
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
handleTerminalUpgrade(req, socket, head, wsSession);
|
|
342
|
+
}
|
|
343
|
+
else if (url.pathname === '/ws/danger-room') {
|
|
344
|
+
// Danger Room: any authenticated user in remote mode (read-only dashboard)
|
|
345
|
+
if (isRemoteMode()) {
|
|
346
|
+
const token = parseSessionCookie(req.headers.cookie);
|
|
347
|
+
const ip = getClientIp(req);
|
|
348
|
+
const session = token ? validateSession(token, ip) : null;
|
|
349
|
+
if (!session) {
|
|
350
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
351
|
+
socket.destroy();
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
handleDangerRoomUpgrade(req, socket, head);
|
|
356
|
+
}
|
|
357
|
+
else if (url.pathname === '/ws/war-room') {
|
|
358
|
+
// War Room: same auth rules as Danger Room
|
|
359
|
+
if (isRemoteMode()) {
|
|
360
|
+
const token = parseSessionCookie(req.headers.cookie);
|
|
361
|
+
const ip = getClientIp(req);
|
|
362
|
+
const session = token ? validateSession(token, ip) : null;
|
|
363
|
+
if (!session) {
|
|
364
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
365
|
+
socket.destroy();
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
handleWarRoomUpgrade(req, socket, head);
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
|
|
373
|
+
socket.destroy();
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
// Local mode: bind to loopback only (PRD §9.20.1) to prevent LAN exposure of vault data.
|
|
377
|
+
// Listen on both 127.0.0.1 (IPv4) and ::1 (IPv6) — macOS resolves 'localhost' to ::1.
|
|
378
|
+
// Remote/LAN mode: bind 0.0.0.0 for external access.
|
|
379
|
+
const bindAddress = (isRemoteMode() || isLanMode()) ? '0.0.0.0' : '127.0.0.1';
|
|
380
|
+
server.listen(port, bindAddress, async () => {
|
|
381
|
+
await initAuditLog();
|
|
382
|
+
startHealthPoller();
|
|
383
|
+
await snapshotNativeModules();
|
|
384
|
+
// In local mode, also listen on IPv6 loopback so macOS 'localhost' (::1) works.
|
|
385
|
+
// TCP proxy from ::1 → 127.0.0.1 so both IPv4 and IPv6 loopback are served.
|
|
386
|
+
if (!isRemoteMode() && !isLanMode()) {
|
|
387
|
+
try {
|
|
388
|
+
const net = await import('node:net');
|
|
389
|
+
ipv6Proxy = net.createServer((socket) => {
|
|
390
|
+
const upstream = net.connect({ port, host: '127.0.0.1' });
|
|
391
|
+
socket.pipe(upstream);
|
|
392
|
+
upstream.pipe(socket);
|
|
393
|
+
socket.on('error', () => upstream.destroy());
|
|
394
|
+
upstream.on('error', () => socket.destroy());
|
|
395
|
+
});
|
|
396
|
+
ipv6Proxy.listen(port, '::1', () => { });
|
|
397
|
+
ipv6Proxy.on('error', () => { }); // Best-effort: IPv6 may not be available
|
|
398
|
+
}
|
|
399
|
+
catch { /* IPv6 loopback best-effort */ }
|
|
400
|
+
}
|
|
401
|
+
resolve();
|
|
402
|
+
});
|
|
403
|
+
// Graceful shutdown — stop health poller, then kill all PTY sessions
|
|
404
|
+
let shuttingDown = false;
|
|
405
|
+
const shutdown = () => {
|
|
406
|
+
if (shuttingDown)
|
|
407
|
+
return;
|
|
408
|
+
shuttingDown = true;
|
|
409
|
+
console.log('\n Shutting down...');
|
|
410
|
+
stopHealthPoller();
|
|
411
|
+
closeDangerRoom();
|
|
412
|
+
closeWarRoom();
|
|
413
|
+
killAllSessions?.();
|
|
414
|
+
if (ipv6Proxy) {
|
|
415
|
+
ipv6Proxy.close();
|
|
416
|
+
ipv6Proxy = null;
|
|
417
|
+
}
|
|
418
|
+
server.close(() => process.exit(0));
|
|
419
|
+
setTimeout(() => process.exit(0), 2000);
|
|
420
|
+
};
|
|
421
|
+
process.on('SIGINT', shutdown);
|
|
422
|
+
process.on('SIGTERM', shutdown);
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
// ── Test mode self-start ────────────────────────────
|
|
426
|
+
// When VOIDFORGE_TEST=1, server.ts can be run directly: `npx tsx wizard/server.ts`
|
|
427
|
+
// This enables Playwright's webServer to start the server without going through the CLI.
|
|
428
|
+
if (process.env['VOIDFORGE_TEST'] === '1') {
|
|
429
|
+
const testPort = parseInt(process.env['PORT'] ?? '3199', 10);
|
|
430
|
+
startServer(testPort).then(() => {
|
|
431
|
+
console.log(` VoidForge test server running on http://127.0.0.1:${testPort}`);
|
|
432
|
+
}).catch((err) => {
|
|
433
|
+
console.error('Test server failed to start:', err);
|
|
434
|
+
process.exit(1);
|
|
435
|
+
});
|
|
436
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "thevoidforge",
|
|
3
|
+
"version": "21.0.0",
|
|
4
|
+
"description": "From nothing, everything. A methodology framework for building with Claude Code.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20.11.0 <25.0.0"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"voidforge": "./dist/scripts/voidforge.js"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"wizard": "npx tsx scripts/voidforge.ts init",
|
|
14
|
+
"deploy": "npx tsx scripts/voidforge.ts deploy",
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"prepack": "bash scripts/prepack-patterns.sh && npm run build",
|
|
17
|
+
"typecheck": "npx tsc --noEmit",
|
|
18
|
+
"test": "vitest run --pool forks",
|
|
19
|
+
"test:watch": "vitest --pool forks",
|
|
20
|
+
"test:coverage": "vitest run --pool forks --coverage",
|
|
21
|
+
"test:e2e": "playwright test",
|
|
22
|
+
"test:e2e:local": "playwright test --project=chromium"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist/",
|
|
26
|
+
"!dist/wizard/__tests__/",
|
|
27
|
+
"!dist/wizard/e2e/",
|
|
28
|
+
"!dist/**/*.js.map",
|
|
29
|
+
"!dist/**/*.d.ts.map"
|
|
30
|
+
],
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"keywords": ["voidforge", "claude-code", "methodology", "ai", "developer-tools"],
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/tmcleod3/voidforge.git",
|
|
39
|
+
"directory": "packages/voidforge"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@aws-sdk/client-ec2": "^3.700.0",
|
|
43
|
+
"@aws-sdk/client-elasticache": "^3.700.0",
|
|
44
|
+
"@aws-sdk/client-rds": "^3.700.0",
|
|
45
|
+
"@aws-sdk/client-s3": "^3.700.0",
|
|
46
|
+
"@aws-sdk/client-sts": "^3.700.0",
|
|
47
|
+
"@types/ws": "^8.18.1",
|
|
48
|
+
"node-pty": "^1.2.0-beta.12",
|
|
49
|
+
"ws": "^8.19.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@axe-core/playwright": "^4.11.1",
|
|
53
|
+
"@playwright/test": "^1.58.2",
|
|
54
|
+
"@types/node": "^25.5.0",
|
|
55
|
+
"tsx": "^4.19.0",
|
|
56
|
+
"typescript": "^5.5.0",
|
|
57
|
+
"vitest": "^3.1.0"
|
|
58
|
+
}
|
|
59
|
+
}
|