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,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Bank Adapter — full implementation for development/demo.
|
|
3
|
+
*
|
|
4
|
+
* Implements RevenueSourceAdapter with realistic fake financial data.
|
|
5
|
+
* Every method returns valid-shaped data. No throws.
|
|
6
|
+
* This IS a full implementation for a sandbox bank (No Stubs Doctrine, v17.0).
|
|
7
|
+
*/
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
9
|
+
export class SandboxBankAdapter {
|
|
10
|
+
accountName;
|
|
11
|
+
balanceCents;
|
|
12
|
+
constructor(label = 'Sandbox Bank', initialBalanceCents = 500000) {
|
|
13
|
+
this.accountName = label;
|
|
14
|
+
this.balanceCents = initialBalanceCents;
|
|
15
|
+
}
|
|
16
|
+
async connect(credentials) {
|
|
17
|
+
return {
|
|
18
|
+
connected: true,
|
|
19
|
+
accountName: this.accountName,
|
|
20
|
+
accountId: `sandbox_bank_${randomUUID().slice(0, 8)}`,
|
|
21
|
+
currency: 'USD',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async getTransactions(dateRange, cursor) {
|
|
25
|
+
// Generate realistic transaction data
|
|
26
|
+
const dayCount = Math.ceil((new Date(dateRange.end).getTime() - new Date(dateRange.start).getTime()) / (24 * 60 * 60 * 1000));
|
|
27
|
+
// Invalid date range (end before start) — return empty result
|
|
28
|
+
if (dayCount <= 0) {
|
|
29
|
+
return {
|
|
30
|
+
transactions: [],
|
|
31
|
+
hasMore: false,
|
|
32
|
+
cursor: undefined,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const transactions = [];
|
|
36
|
+
// Simulate 2-5 transactions per day
|
|
37
|
+
for (let d = 0; d < dayCount; d++) {
|
|
38
|
+
const date = new Date(new Date(dateRange.start).getTime() + d * 86400000);
|
|
39
|
+
const txCount = 2 + Math.floor(Math.random() * 4);
|
|
40
|
+
for (let t = 0; t < txCount; t++) {
|
|
41
|
+
const isRevenue = Math.random() > 0.4; // 60% revenue, 40% expenses
|
|
42
|
+
const amountCents = isRevenue
|
|
43
|
+
? Math.round(500 + Math.random() * 9500) // $5-$100 revenue
|
|
44
|
+
: -Math.round(100 + Math.random() * 5000); // $1-$50 expenses
|
|
45
|
+
const descriptions = isRevenue
|
|
46
|
+
? ['Stripe payout', 'Customer payment', 'Subscription renewal', 'One-time purchase']
|
|
47
|
+
: ['Ad spend - Google', 'Ad spend - Meta', 'SaaS subscription', 'Domain renewal', 'Hosting'];
|
|
48
|
+
transactions.push({
|
|
49
|
+
externalId: `txn_${randomUUID().slice(0, 12)}`,
|
|
50
|
+
type: isRevenue ? 'charge' : 'refund',
|
|
51
|
+
amount: amountCents,
|
|
52
|
+
currency: 'USD',
|
|
53
|
+
description: descriptions[Math.floor(Math.random() * descriptions.length)],
|
|
54
|
+
metadata: { sandbox: 'true', category: isRevenue ? 'revenue' : 'expense' },
|
|
55
|
+
createdAt: date.toISOString(),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
transactions,
|
|
61
|
+
hasMore: false,
|
|
62
|
+
cursor: undefined,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async getBalance() {
|
|
66
|
+
// Slightly vary the balance to simulate real account activity
|
|
67
|
+
this.balanceCents += Math.round((Math.random() - 0.3) * 10000);
|
|
68
|
+
return {
|
|
69
|
+
available: this.balanceCents,
|
|
70
|
+
pending: Math.round(Math.random() * 50000),
|
|
71
|
+
currency: 'USD',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async detectCurrency(_credentials) {
|
|
75
|
+
return 'USD';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Ad Platform Adapter — full implementation for development/demo.
|
|
3
|
+
*
|
|
4
|
+
* Returns realistic fake data matching the AdPlatformSetup + AdPlatformAdapter
|
|
5
|
+
* interfaces. Every method returns valid-shaped data. No throws.
|
|
6
|
+
* This IS a full implementation for a sandbox platform (No Stubs Doctrine, v17.0).
|
|
7
|
+
*
|
|
8
|
+
* Use case: demonstrates the full Cultivation pipeline end-to-end without
|
|
9
|
+
* real platform API credentials. Users can see data flowing through dashboards,
|
|
10
|
+
* heartbeat daemon running real jobs, reconciliation processing, circuit breakers firing.
|
|
11
|
+
*/
|
|
12
|
+
import type { AdPlatformSetup, AdPlatformAdapter, OAuthTokens, ConnectionStatus, CampaignConfig, CampaignResult, CampaignUpdate, CreativeConfig, SpendReport, PerformanceMetrics, InsightData, Cents } from './types.js';
|
|
13
|
+
export declare class SandboxSetup implements AdPlatformSetup {
|
|
14
|
+
private label;
|
|
15
|
+
constructor(label?: string);
|
|
16
|
+
authenticate(): Promise<OAuthTokens>;
|
|
17
|
+
verifyConnection(tokens: OAuthTokens): Promise<ConnectionStatus>;
|
|
18
|
+
detectCurrency(_tokens: OAuthTokens): Promise<string>;
|
|
19
|
+
}
|
|
20
|
+
export declare class SandboxAdapter implements AdPlatformAdapter {
|
|
21
|
+
private accountId;
|
|
22
|
+
private tokens;
|
|
23
|
+
private campaigns;
|
|
24
|
+
constructor(accountId?: string, tokens?: OAuthTokens);
|
|
25
|
+
refreshToken(token: OAuthTokens): Promise<OAuthTokens>;
|
|
26
|
+
createCampaign(config: CampaignConfig): Promise<CampaignResult>;
|
|
27
|
+
updateCampaign(id: string, changes: CampaignUpdate): Promise<void>;
|
|
28
|
+
pauseCampaign(id: string): Promise<void>;
|
|
29
|
+
resumeCampaign(id: string): Promise<void>;
|
|
30
|
+
deleteCampaign(id: string): Promise<void>;
|
|
31
|
+
updateBudget(id: string, dailyBudget: Cents): Promise<void>;
|
|
32
|
+
updateCreative(_id: string, _creative: CreativeConfig): Promise<void>;
|
|
33
|
+
getSpend(dateRange: {
|
|
34
|
+
start: string;
|
|
35
|
+
end: string;
|
|
36
|
+
}): Promise<SpendReport>;
|
|
37
|
+
getPerformance(campaignId: string): Promise<PerformanceMetrics>;
|
|
38
|
+
getInsights(campaignId: string, metrics: string[]): Promise<InsightData>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Ad Platform Adapter — full implementation for development/demo.
|
|
3
|
+
*
|
|
4
|
+
* Returns realistic fake data matching the AdPlatformSetup + AdPlatformAdapter
|
|
5
|
+
* interfaces. Every method returns valid-shaped data. No throws.
|
|
6
|
+
* This IS a full implementation for a sandbox platform (No Stubs Doctrine, v17.0).
|
|
7
|
+
*
|
|
8
|
+
* Use case: demonstrates the full Cultivation pipeline end-to-end without
|
|
9
|
+
* real platform API credentials. Users can see data flowing through dashboards,
|
|
10
|
+
* heartbeat daemon running real jobs, reconciliation processing, circuit breakers firing.
|
|
11
|
+
*/
|
|
12
|
+
import { randomUUID } from 'node:crypto';
|
|
13
|
+
import { toCents } from './types.js';
|
|
14
|
+
export class SandboxSetup {
|
|
15
|
+
label;
|
|
16
|
+
constructor(label = 'Sandbox') {
|
|
17
|
+
this.label = label;
|
|
18
|
+
}
|
|
19
|
+
async authenticate() {
|
|
20
|
+
return {
|
|
21
|
+
accessToken: `sandbox_access_${randomUUID().slice(0, 8)}`,
|
|
22
|
+
refreshToken: `sandbox_refresh_${randomUUID().slice(0, 8)}`,
|
|
23
|
+
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(),
|
|
24
|
+
platform: 'meta',
|
|
25
|
+
scopes: ['ads_management', 'ads_read'],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async verifyConnection(tokens) {
|
|
29
|
+
return {
|
|
30
|
+
connected: true,
|
|
31
|
+
accountName: `${this.label} Demo Account`,
|
|
32
|
+
accountId: 'sandbox_' + randomUUID().slice(0, 8),
|
|
33
|
+
currency: 'USD',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async detectCurrency(_tokens) {
|
|
37
|
+
return 'USD';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export class SandboxAdapter {
|
|
41
|
+
accountId;
|
|
42
|
+
tokens;
|
|
43
|
+
// Instance-level campaign store — prevents state leaks between test runs and adapter instances
|
|
44
|
+
campaigns = new Map();
|
|
45
|
+
constructor(accountId = 'sandbox', tokens = { accessToken: '', refreshToken: '', expiresAt: '', platform: 'meta', scopes: [] }) {
|
|
46
|
+
this.accountId = accountId;
|
|
47
|
+
this.tokens = tokens;
|
|
48
|
+
}
|
|
49
|
+
async refreshToken(token) {
|
|
50
|
+
return {
|
|
51
|
+
...token,
|
|
52
|
+
accessToken: `sandbox_access_${randomUUID().slice(0, 8)}`,
|
|
53
|
+
expiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async createCampaign(config) {
|
|
57
|
+
const id = `sandbox_camp_${randomUUID().slice(0, 12)}`;
|
|
58
|
+
this.campaigns.set(id, {
|
|
59
|
+
id,
|
|
60
|
+
name: config.name,
|
|
61
|
+
status: 'paused',
|
|
62
|
+
dailyBudgetCents: config.dailyBudget ?? 1000,
|
|
63
|
+
createdAt: new Date().toISOString(),
|
|
64
|
+
totalSpendCents: 0,
|
|
65
|
+
impressions: 0,
|
|
66
|
+
clicks: 0,
|
|
67
|
+
conversions: 0,
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
externalId: id,
|
|
71
|
+
platform: config.platform ?? 'meta',
|
|
72
|
+
status: 'created',
|
|
73
|
+
dashboardUrl: `https://sandbox.voidforge.dev/campaigns/${id}`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async updateCampaign(id, changes) {
|
|
77
|
+
const camp = this.campaigns.get(id);
|
|
78
|
+
if (camp) {
|
|
79
|
+
if (changes.name)
|
|
80
|
+
camp.name = changes.name;
|
|
81
|
+
if (changes.dailyBudget)
|
|
82
|
+
camp.dailyBudgetCents = changes.dailyBudget;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async pauseCampaign(id) {
|
|
86
|
+
const camp = this.campaigns.get(id);
|
|
87
|
+
if (camp)
|
|
88
|
+
camp.status = 'paused';
|
|
89
|
+
}
|
|
90
|
+
async resumeCampaign(id) {
|
|
91
|
+
const camp = this.campaigns.get(id);
|
|
92
|
+
if (camp)
|
|
93
|
+
camp.status = 'active';
|
|
94
|
+
}
|
|
95
|
+
async deleteCampaign(id) {
|
|
96
|
+
const camp = this.campaigns.get(id);
|
|
97
|
+
if (camp)
|
|
98
|
+
camp.status = 'deleted';
|
|
99
|
+
}
|
|
100
|
+
async updateBudget(id, dailyBudget) {
|
|
101
|
+
const camp = this.campaigns.get(id);
|
|
102
|
+
if (camp)
|
|
103
|
+
camp.dailyBudgetCents = dailyBudget;
|
|
104
|
+
}
|
|
105
|
+
async updateCreative(_id, _creative) {
|
|
106
|
+
// Sandbox accepts any creative update
|
|
107
|
+
}
|
|
108
|
+
async getSpend(dateRange) {
|
|
109
|
+
// Generate realistic spend data based on active campaigns
|
|
110
|
+
const activeCampaigns = [...this.campaigns.values()].filter(c => c.status === 'active');
|
|
111
|
+
const dayCount = Math.ceil((new Date(dateRange.end).getTime() - new Date(dateRange.start).getTime()) / (24 * 60 * 60 * 1000));
|
|
112
|
+
// Invalid date range (end before start) — return empty result
|
|
113
|
+
if (dayCount <= 0) {
|
|
114
|
+
return {
|
|
115
|
+
platform: 'meta',
|
|
116
|
+
dateRange,
|
|
117
|
+
totalSpend: toCents(0),
|
|
118
|
+
campaigns: [],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const campaignSpend = activeCampaigns.map(c => {
|
|
122
|
+
// Simulate realistic daily spend (~70-95% of budget)
|
|
123
|
+
const spendFactor = 0.7 + Math.random() * 0.25;
|
|
124
|
+
const dailySpend = Math.round(c.dailyBudgetCents * spendFactor);
|
|
125
|
+
const totalCampaignSpend = dailySpend * dayCount;
|
|
126
|
+
c.totalSpendCents += totalCampaignSpend;
|
|
127
|
+
c.impressions += Math.round(totalCampaignSpend / 2); // ~$0.02 CPM
|
|
128
|
+
c.clicks += Math.round(c.impressions * (0.01 + Math.random() * 0.03)); // 1-4% CTR
|
|
129
|
+
c.conversions += Math.round(c.clicks * (0.02 + Math.random() * 0.05)); // 2-7% CVR
|
|
130
|
+
return {
|
|
131
|
+
externalId: c.id,
|
|
132
|
+
spend: toCents(totalCampaignSpend / 100),
|
|
133
|
+
impressions: c.impressions,
|
|
134
|
+
clicks: c.clicks,
|
|
135
|
+
conversions: c.conversions,
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
const totalSpendValue = campaignSpend.reduce((sum, c) => sum + c.spend, 0);
|
|
139
|
+
return {
|
|
140
|
+
platform: 'meta',
|
|
141
|
+
dateRange,
|
|
142
|
+
totalSpend: toCents(totalSpendValue / 100),
|
|
143
|
+
campaigns: campaignSpend,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
async getPerformance(campaignId) {
|
|
147
|
+
const camp = this.campaigns.get(campaignId);
|
|
148
|
+
const impressions = camp?.impressions ?? Math.round(1000 + Math.random() * 9000);
|
|
149
|
+
const clicks = camp?.clicks ?? Math.round(impressions * 0.025);
|
|
150
|
+
const conversions = camp?.conversions ?? Math.round(clicks * 0.04);
|
|
151
|
+
const spendCentsRaw = camp?.totalSpendCents ?? Math.round(impressions * 2);
|
|
152
|
+
return {
|
|
153
|
+
campaignId,
|
|
154
|
+
impressions,
|
|
155
|
+
clicks,
|
|
156
|
+
conversions,
|
|
157
|
+
spend: toCents(spendCentsRaw / 100),
|
|
158
|
+
ctr: (clicks / Math.max(impressions, 1)),
|
|
159
|
+
cpc: toCents((spendCentsRaw / Math.max(clicks, 1)) / 100),
|
|
160
|
+
roas: (conversions > 0 ? (conversions * 5000) / Math.max(spendCentsRaw, 1) : 0),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async getInsights(campaignId, metrics) {
|
|
164
|
+
const perf = await this.getPerformance(campaignId);
|
|
165
|
+
const data = {};
|
|
166
|
+
for (const metric of metrics) {
|
|
167
|
+
if (metric in perf)
|
|
168
|
+
data[metric] = perf[metric];
|
|
169
|
+
else
|
|
170
|
+
data[metric] = 0;
|
|
171
|
+
}
|
|
172
|
+
return { campaignId, metrics: data };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripe Revenue Adapter — real implementation via node:https (zero new dependencies).
|
|
3
|
+
*
|
|
4
|
+
* Implements RevenueSourceAdapter for Stripe's REST API.
|
|
5
|
+
* Read-only: VoidForge never processes payments, only reads revenue data.
|
|
6
|
+
* Uses Stripe API v1 (stable, well-documented, free test mode).
|
|
7
|
+
*
|
|
8
|
+
* PRD Reference: §9.4, §9.9 (revenue tracking)
|
|
9
|
+
* No Stubs Doctrine: every method makes a real API call.
|
|
10
|
+
*/
|
|
11
|
+
import type { RevenueSourceAdapter, RevenueCredentials, ConnectionResult, TransactionPage, BalanceResult, DateRange } from '../revenue-types.js';
|
|
12
|
+
export declare class StripeAdapter implements RevenueSourceAdapter {
|
|
13
|
+
private apiKey;
|
|
14
|
+
constructor(apiKey: string);
|
|
15
|
+
connect(_credentials?: RevenueCredentials): Promise<ConnectionResult>;
|
|
16
|
+
detectCurrency(_credentials?: RevenueCredentials): Promise<string>;
|
|
17
|
+
getTransactions(range: DateRange, cursor?: string): Promise<TransactionPage>;
|
|
18
|
+
getBalance(): Promise<BalanceResult>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripe Revenue Adapter — real implementation via node:https (zero new dependencies).
|
|
3
|
+
*
|
|
4
|
+
* Implements RevenueSourceAdapter for Stripe's REST API.
|
|
5
|
+
* Read-only: VoidForge never processes payments, only reads revenue data.
|
|
6
|
+
* Uses Stripe API v1 (stable, well-documented, free test mode).
|
|
7
|
+
*
|
|
8
|
+
* PRD Reference: §9.4, §9.9 (revenue tracking)
|
|
9
|
+
* No Stubs Doctrine: every method makes a real API call.
|
|
10
|
+
*/
|
|
11
|
+
import { request as httpsRequest } from 'node:https';
|
|
12
|
+
const STRIPE_HOST = 'api.stripe.com';
|
|
13
|
+
/** Make a GET request to the Stripe API. */
|
|
14
|
+
async function stripeGet(path, apiKey, params) {
|
|
15
|
+
const query = params ? '?' + new URLSearchParams(params).toString() : '';
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const req = httpsRequest({
|
|
18
|
+
hostname: STRIPE_HOST,
|
|
19
|
+
path: `/v1${path}${query}`,
|
|
20
|
+
method: 'GET',
|
|
21
|
+
headers: {
|
|
22
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
23
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
24
|
+
},
|
|
25
|
+
timeout: 15000,
|
|
26
|
+
}, (res) => {
|
|
27
|
+
let data = '';
|
|
28
|
+
res.on('data', (chunk) => { data += chunk.toString(); });
|
|
29
|
+
res.on('end', () => resolve({ status: res.statusCode ?? 500, body: data }));
|
|
30
|
+
});
|
|
31
|
+
req.on('error', reject);
|
|
32
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Stripe API timeout')); });
|
|
33
|
+
req.end();
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export class StripeAdapter {
|
|
37
|
+
apiKey;
|
|
38
|
+
constructor(apiKey) {
|
|
39
|
+
this.apiKey = apiKey;
|
|
40
|
+
}
|
|
41
|
+
async connect(_credentials) {
|
|
42
|
+
try {
|
|
43
|
+
const { status, body } = await stripeGet('/account', this.apiKey);
|
|
44
|
+
if (status === 200) {
|
|
45
|
+
const account = JSON.parse(body);
|
|
46
|
+
return {
|
|
47
|
+
connected: true,
|
|
48
|
+
accountId: account.id,
|
|
49
|
+
accountName: account.settings?.dashboard?.display_name
|
|
50
|
+
?? account.business_profile?.name
|
|
51
|
+
?? account.id
|
|
52
|
+
?? 'Stripe Account',
|
|
53
|
+
currency: (account.default_currency ?? 'usd').toUpperCase(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// VG-R1-005: Wrap error-path JSON.parse — response may be non-JSON (e.g., HTML from proxy 502)
|
|
57
|
+
try {
|
|
58
|
+
const error = JSON.parse(body);
|
|
59
|
+
return { connected: false, error: error.error?.message ?? `HTTP ${status}` };
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return { connected: false, error: `HTTP ${status}` };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
return { connected: false, error: err instanceof Error ? err.message : 'Connection failed' };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async detectCurrency(_credentials) {
|
|
70
|
+
const result = await this.connect();
|
|
71
|
+
return result.currency ?? 'USD';
|
|
72
|
+
}
|
|
73
|
+
async getTransactions(range, cursor) {
|
|
74
|
+
try {
|
|
75
|
+
const params = {
|
|
76
|
+
limit: '100',
|
|
77
|
+
'created[gte]': String(Math.floor(new Date(range.start).getTime() / 1000)),
|
|
78
|
+
'created[lte]': String(Math.floor(new Date(range.end).getTime() / 1000)),
|
|
79
|
+
};
|
|
80
|
+
if (cursor)
|
|
81
|
+
params.starting_after = cursor;
|
|
82
|
+
const { status, body } = await stripeGet('/charges', this.apiKey, params);
|
|
83
|
+
if (status !== 200) {
|
|
84
|
+
// VG-R1-005: Wrap error-path JSON.parse — response may be non-JSON (e.g., HTML from proxy 502)
|
|
85
|
+
let errorMsg = `HTTP ${status}`;
|
|
86
|
+
try {
|
|
87
|
+
const error = JSON.parse(body);
|
|
88
|
+
errorMsg = error.error?.message ?? errorMsg;
|
|
89
|
+
}
|
|
90
|
+
catch { /* non-JSON response — use raw HTTP status */ }
|
|
91
|
+
throw new Error(errorMsg);
|
|
92
|
+
}
|
|
93
|
+
const data = JSON.parse(body);
|
|
94
|
+
const transactions = data.data
|
|
95
|
+
.filter(charge => charge.status === 'succeeded')
|
|
96
|
+
.map(charge => ({
|
|
97
|
+
externalId: charge.id,
|
|
98
|
+
type: 'charge',
|
|
99
|
+
amount: charge.amount, // Stripe amounts are already in cents
|
|
100
|
+
currency: 'USD',
|
|
101
|
+
description: charge.description ?? 'Stripe charge',
|
|
102
|
+
metadata: charge.metadata ?? {},
|
|
103
|
+
createdAt: new Date(charge.created * 1000).toISOString(),
|
|
104
|
+
}));
|
|
105
|
+
const lastId = data.data.length > 0 ? data.data[data.data.length - 1].id : undefined;
|
|
106
|
+
return {
|
|
107
|
+
transactions,
|
|
108
|
+
hasMore: data.has_more,
|
|
109
|
+
cursor: data.has_more ? lastId : undefined,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
throw new Error(`Stripe getTransactions failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async getBalance() {
|
|
117
|
+
try {
|
|
118
|
+
const { status, body } = await stripeGet('/balance', this.apiKey);
|
|
119
|
+
if (status !== 200) {
|
|
120
|
+
// VG-R1-005: Wrap error-path JSON.parse — response may be non-JSON (e.g., HTML from proxy 502)
|
|
121
|
+
let errorMsg = `HTTP ${status}`;
|
|
122
|
+
try {
|
|
123
|
+
const error = JSON.parse(body);
|
|
124
|
+
errorMsg = error.error?.message ?? errorMsg;
|
|
125
|
+
}
|
|
126
|
+
catch { /* non-JSON response — use raw HTTP status */ }
|
|
127
|
+
throw new Error(errorMsg);
|
|
128
|
+
}
|
|
129
|
+
const data = JSON.parse(body);
|
|
130
|
+
// Sum across all currencies (convert to USD cents — in practice, most accounts use one currency)
|
|
131
|
+
const availableTotal = data.available.reduce((sum, b) => sum + b.amount, 0);
|
|
132
|
+
const pendingTotal = data.pending.reduce((sum, b) => sum + b.amount, 0);
|
|
133
|
+
return {
|
|
134
|
+
available: availableTotal,
|
|
135
|
+
pending: pendingTotal,
|
|
136
|
+
currency: 'USD',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
throw new Error(`Stripe getBalance failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ad Platform Adapter Types — shared across all platform implementations.
|
|
3
|
+
* Re-exports from the pattern file for convenience.
|
|
4
|
+
*
|
|
5
|
+
* PRD Reference: §9.5, §9.19.10, §9.20.4
|
|
6
|
+
*/
|
|
7
|
+
export type { AdPlatformSetup, AdPlatformAdapter, ReadOnlyAdapter, CampaignConfig, CampaignResult, CampaignUpdate, CreativeConfig, SpendReport, PerformanceMetrics, InsightData, OAuthTokens, ConnectionStatus, PlatformError, Cents, Percentage, Ratio, AdPlatform, } from '../ad-platform-core.js';
|
|
8
|
+
export { toCents, toDollars } from '../ad-platform-core.js';
|
|
9
|
+
export { OutboundRateLimiter, getLimiter } from '../rate-limiter-core.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ad Platform Adapter Types — shared across all platform implementations.
|
|
3
|
+
* Re-exports from the pattern file for convenience.
|
|
4
|
+
*
|
|
5
|
+
* PRD Reference: §9.5, §9.19.10, §9.20.4
|
|
6
|
+
*/
|
|
7
|
+
export { toCents, toDollars } from '../ad-platform-core.js';
|
|
8
|
+
// Note: TokenBucketLimiter in ad-platform-adapter.ts is the simple reference version.
|
|
9
|
+
// Production adapters use OutboundRateLimiter (below) which has safety margins, daily quotas, and retry logic.
|
|
10
|
+
export { OutboundRateLimiter, getLimiter } from '../rate-limiter-core.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Memory — Cross-project lesson storage and retrieval.
|
|
3
|
+
*
|
|
4
|
+
* After each build, key learnings are stored here. When starting a new build,
|
|
5
|
+
* relevant lessons are loaded into methodology context.
|
|
6
|
+
* "Last time you built a Next.js app with Stripe, Phase 6 failed because
|
|
7
|
+
* webhook signatures weren't verified in test mode."
|
|
8
|
+
*
|
|
9
|
+
* Wong guards the knowledge. The Sanctum grows.
|
|
10
|
+
*
|
|
11
|
+
* Storage: ~/.voidforge/lessons.json (0600 permissions).
|
|
12
|
+
* Never stores credentials or PII — only methodology learnings.
|
|
13
|
+
*/
|
|
14
|
+
export interface Lesson {
|
|
15
|
+
id: string;
|
|
16
|
+
framework: string;
|
|
17
|
+
category: string;
|
|
18
|
+
lesson: string;
|
|
19
|
+
action: string;
|
|
20
|
+
project: string;
|
|
21
|
+
agent: string;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
}
|
|
24
|
+
export type LessonInput = Omit<Lesson, 'id' | 'createdAt'>;
|
|
25
|
+
/** Add a lesson. Returns the created lesson with ID and timestamp. */
|
|
26
|
+
export declare function addLesson(input: LessonInput): Promise<Lesson>;
|
|
27
|
+
/** Get all lessons, optionally filtered. */
|
|
28
|
+
export declare function getLessons(filters?: {
|
|
29
|
+
framework?: string;
|
|
30
|
+
category?: string;
|
|
31
|
+
project?: string;
|
|
32
|
+
}): Promise<Lesson[]>;
|
|
33
|
+
/** Get lessons relevant to a build context (by framework). */
|
|
34
|
+
export declare function getRelevantLessons(framework: string): Promise<Lesson[]>;
|
|
35
|
+
/** Get total lesson count. */
|
|
36
|
+
export declare function getLessonCount(): Promise<number>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Memory — Cross-project lesson storage and retrieval.
|
|
3
|
+
*
|
|
4
|
+
* After each build, key learnings are stored here. When starting a new build,
|
|
5
|
+
* relevant lessons are loaded into methodology context.
|
|
6
|
+
* "Last time you built a Next.js app with Stripe, Phase 6 failed because
|
|
7
|
+
* webhook signatures weren't verified in test mode."
|
|
8
|
+
*
|
|
9
|
+
* Wong guards the knowledge. The Sanctum grows.
|
|
10
|
+
*
|
|
11
|
+
* Storage: ~/.voidforge/lessons.json (0600 permissions).
|
|
12
|
+
* Never stores credentials or PII — only methodology learnings.
|
|
13
|
+
*/
|
|
14
|
+
import { readFile, rename, mkdir, open } from 'node:fs/promises';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { randomUUID } from 'node:crypto';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
const VOIDFORGE_DIR = join(homedir(), '.voidforge');
|
|
19
|
+
const LESSONS_PATH = join(VOIDFORGE_DIR, 'lessons.json');
|
|
20
|
+
const MAX_LESSONS = 1000; // Cap to prevent unbounded growth
|
|
21
|
+
// ── Write serialization ────────────────────────────
|
|
22
|
+
let writeQueue = Promise.resolve();
|
|
23
|
+
function serialized(fn) {
|
|
24
|
+
const result = writeQueue.then(fn, () => fn());
|
|
25
|
+
writeQueue = result.then(() => { }, () => { });
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
// ── File I/O ───────────────────────────────────────
|
|
29
|
+
async function readLessons() {
|
|
30
|
+
try {
|
|
31
|
+
const raw = await readFile(LESSONS_PATH, 'utf-8');
|
|
32
|
+
const parsed = JSON.parse(raw);
|
|
33
|
+
if (!Array.isArray(parsed))
|
|
34
|
+
return [];
|
|
35
|
+
return parsed.filter(isValidLesson);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`Lessons file corrupted: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function writeLessons(lessons) {
|
|
45
|
+
await mkdir(VOIDFORGE_DIR, { recursive: true });
|
|
46
|
+
const data = JSON.stringify(lessons, null, 2);
|
|
47
|
+
const tmpPath = LESSONS_PATH + '.tmp';
|
|
48
|
+
const fh = await open(tmpPath, 'w', 0o600);
|
|
49
|
+
try {
|
|
50
|
+
await fh.writeFile(data);
|
|
51
|
+
await fh.sync();
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
await fh.close();
|
|
55
|
+
}
|
|
56
|
+
await rename(tmpPath, LESSONS_PATH);
|
|
57
|
+
}
|
|
58
|
+
function isValidLesson(obj) {
|
|
59
|
+
if (typeof obj !== 'object' || obj === null)
|
|
60
|
+
return false;
|
|
61
|
+
const l = obj;
|
|
62
|
+
return (typeof l.id === 'string' &&
|
|
63
|
+
typeof l.framework === 'string' &&
|
|
64
|
+
typeof l.category === 'string' &&
|
|
65
|
+
typeof l.lesson === 'string' &&
|
|
66
|
+
typeof l.action === 'string' &&
|
|
67
|
+
typeof l.project === 'string' &&
|
|
68
|
+
typeof l.agent === 'string' &&
|
|
69
|
+
typeof l.createdAt === 'string');
|
|
70
|
+
}
|
|
71
|
+
// ── Public API ─────────────────────────────────────
|
|
72
|
+
/** Add a lesson. Returns the created lesson with ID and timestamp. */
|
|
73
|
+
export function addLesson(input) {
|
|
74
|
+
return serialized(async () => {
|
|
75
|
+
const lessons = await readLessons();
|
|
76
|
+
const lesson = {
|
|
77
|
+
...input,
|
|
78
|
+
id: randomUUID(),
|
|
79
|
+
createdAt: new Date().toISOString(),
|
|
80
|
+
};
|
|
81
|
+
lessons.push(lesson);
|
|
82
|
+
// Evict oldest if over cap
|
|
83
|
+
if (lessons.length > MAX_LESSONS) {
|
|
84
|
+
lessons.splice(0, lessons.length - MAX_LESSONS);
|
|
85
|
+
}
|
|
86
|
+
await writeLessons(lessons);
|
|
87
|
+
return lesson;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** Get all lessons, optionally filtered. */
|
|
91
|
+
export async function getLessons(filters) {
|
|
92
|
+
const lessons = await readLessons();
|
|
93
|
+
if (!filters)
|
|
94
|
+
return lessons;
|
|
95
|
+
return lessons.filter((l) => {
|
|
96
|
+
if (filters.framework && l.framework !== filters.framework)
|
|
97
|
+
return false;
|
|
98
|
+
if (filters.category && l.category !== filters.category)
|
|
99
|
+
return false;
|
|
100
|
+
if (filters.project && l.project !== filters.project)
|
|
101
|
+
return false;
|
|
102
|
+
return true;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/** Get lessons relevant to a build context (by framework). */
|
|
106
|
+
export async function getRelevantLessons(framework) {
|
|
107
|
+
const lessons = await readLessons();
|
|
108
|
+
return lessons.filter((l) => l.framework === framework || l.framework === 'any');
|
|
109
|
+
}
|
|
110
|
+
/** Get total lesson count. */
|
|
111
|
+
export async function getLessonCount() {
|
|
112
|
+
const lessons = await readLessons();
|
|
113
|
+
return lessons.length;
|
|
114
|
+
}
|