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,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mercury Bank Adapter — real implementation via node:https (zero new dependencies).
|
|
3
|
+
*
|
|
4
|
+
* Implements RevenueSourceAdapter for Mercury's REST API.
|
|
5
|
+
* Mercury is a bank, not a stablecoin provider — this reads bank balances
|
|
6
|
+
* and transactions for treasury visibility (same pattern as Stripe adapter).
|
|
7
|
+
*
|
|
8
|
+
* Mercury API v1:
|
|
9
|
+
* Base URL: https://api.mercury.com/api/v1
|
|
10
|
+
* Auth: Authorization: Bearer {apiKey}
|
|
11
|
+
*
|
|
12
|
+
* PRD Reference: §12.2 (Operating Bank Account)
|
|
13
|
+
* No Stubs Doctrine: every method makes a real API call.
|
|
14
|
+
*/
|
|
15
|
+
import { request as httpsRequest } from 'node:https';
|
|
16
|
+
const MERCURY_HOST = 'api.mercury.com';
|
|
17
|
+
// ── HTTP helper ──────────────────────────────────────
|
|
18
|
+
async function mercuryGet(path, apiKey, params) {
|
|
19
|
+
const query = params ? '?' + new URLSearchParams(params).toString() : '';
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const req = httpsRequest({
|
|
22
|
+
hostname: MERCURY_HOST,
|
|
23
|
+
path: `/api/v1${path}${query}`,
|
|
24
|
+
method: 'GET',
|
|
25
|
+
headers: {
|
|
26
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
27
|
+
'Accept': 'application/json',
|
|
28
|
+
},
|
|
29
|
+
timeout: 15000,
|
|
30
|
+
}, (res) => {
|
|
31
|
+
let data = '';
|
|
32
|
+
res.on('data', (chunk) => { data += chunk.toString(); });
|
|
33
|
+
res.on('end', () => resolve({ status: res.statusCode ?? 500, body: data }));
|
|
34
|
+
});
|
|
35
|
+
req.on('error', reject);
|
|
36
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Mercury API timeout')); });
|
|
37
|
+
req.end();
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// ── Mercury Bank Adapter ─────────────────────────────
|
|
41
|
+
export class MercuryBankAdapter {
|
|
42
|
+
apiKey;
|
|
43
|
+
accountId;
|
|
44
|
+
constructor(apiKey, accountId) {
|
|
45
|
+
this.apiKey = apiKey;
|
|
46
|
+
this.accountId = accountId;
|
|
47
|
+
}
|
|
48
|
+
async connect(_credentials) {
|
|
49
|
+
try {
|
|
50
|
+
const { status, body } = await mercuryGet('/accounts', this.apiKey);
|
|
51
|
+
if (status === 200) {
|
|
52
|
+
const data = JSON.parse(body);
|
|
53
|
+
const accounts = data.accounts ?? [];
|
|
54
|
+
if (accounts.length === 0) {
|
|
55
|
+
return { connected: false, error: 'No accounts found' };
|
|
56
|
+
}
|
|
57
|
+
const primary = accounts[0];
|
|
58
|
+
this.accountId = primary.id;
|
|
59
|
+
return {
|
|
60
|
+
connected: true,
|
|
61
|
+
accountId: primary.id,
|
|
62
|
+
accountName: primary.name ?? 'Mercury Account',
|
|
63
|
+
currency: 'USD',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// VG-R1-005: Wrap error-path JSON.parse
|
|
67
|
+
try {
|
|
68
|
+
const error = JSON.parse(body);
|
|
69
|
+
return { connected: false, error: error.message ?? error.error ?? `HTTP ${status}` };
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return { connected: false, error: `HTTP ${status}` };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
return { connected: false, error: err instanceof Error ? err.message : 'Connection failed' };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async detectCurrency(_credentials) {
|
|
80
|
+
// Mercury is USD-only
|
|
81
|
+
return 'USD';
|
|
82
|
+
}
|
|
83
|
+
async getTransactions(range, cursor) {
|
|
84
|
+
if (!this.accountId) {
|
|
85
|
+
throw new Error('Mercury: must call connect() before getTransactions()');
|
|
86
|
+
}
|
|
87
|
+
const params = {
|
|
88
|
+
start: range.start,
|
|
89
|
+
end: range.end,
|
|
90
|
+
limit: '100',
|
|
91
|
+
};
|
|
92
|
+
if (cursor)
|
|
93
|
+
params.offset = cursor;
|
|
94
|
+
try {
|
|
95
|
+
const { status, body } = await mercuryGet(`/account/${this.accountId}/transactions`, this.apiKey, params);
|
|
96
|
+
if (status !== 200) {
|
|
97
|
+
// VG-R1-005: Wrap error-path JSON.parse
|
|
98
|
+
let errorMsg = `HTTP ${status}`;
|
|
99
|
+
try {
|
|
100
|
+
const error = JSON.parse(body);
|
|
101
|
+
errorMsg = error.message ?? errorMsg;
|
|
102
|
+
}
|
|
103
|
+
catch { /* non-JSON response */ }
|
|
104
|
+
throw new Error(errorMsg);
|
|
105
|
+
}
|
|
106
|
+
const data = JSON.parse(body);
|
|
107
|
+
const txns = data.transactions ?? [];
|
|
108
|
+
const transactions = txns
|
|
109
|
+
.filter(t => t.status === 'sent' || t.status === 'received' || t.status === 'pending')
|
|
110
|
+
.map(t => ({
|
|
111
|
+
externalId: t.id ?? '',
|
|
112
|
+
type: mapMercuryKind(t.kind, t.amount),
|
|
113
|
+
amount: Math.round((t.amount ?? 0) * 100),
|
|
114
|
+
currency: 'USD',
|
|
115
|
+
description: t.note ?? t.counterpartyName ?? 'Mercury transaction',
|
|
116
|
+
metadata: {},
|
|
117
|
+
createdAt: t.createdAt ?? new Date().toISOString(),
|
|
118
|
+
}));
|
|
119
|
+
const total = data.total ?? 0;
|
|
120
|
+
const offset = cursor ? parseInt(cursor, 10) : 0;
|
|
121
|
+
const hasMore = offset + txns.length < total;
|
|
122
|
+
const nextCursor = hasMore ? String(offset + txns.length) : undefined;
|
|
123
|
+
return {
|
|
124
|
+
transactions,
|
|
125
|
+
hasMore,
|
|
126
|
+
cursor: nextCursor,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
throw new Error(`Mercury getTransactions failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async getBalance() {
|
|
134
|
+
if (!this.accountId) {
|
|
135
|
+
throw new Error('Mercury: must call connect() before getBalance()');
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const { status, body } = await mercuryGet(`/account/${this.accountId}`, this.apiKey);
|
|
139
|
+
if (status !== 200) {
|
|
140
|
+
let errorMsg = `HTTP ${status}`;
|
|
141
|
+
try {
|
|
142
|
+
const error = JSON.parse(body);
|
|
143
|
+
errorMsg = error.message ?? errorMsg;
|
|
144
|
+
}
|
|
145
|
+
catch { /* non-JSON response */ }
|
|
146
|
+
throw new Error(errorMsg);
|
|
147
|
+
}
|
|
148
|
+
const data = JSON.parse(body);
|
|
149
|
+
const available = Math.round((data.availableBalance ?? data.currentBalance ?? 0) * 100);
|
|
150
|
+
const pending = Math.round(((data.currentBalance ?? 0) - (data.availableBalance ?? data.currentBalance ?? 0)) * 100);
|
|
151
|
+
return {
|
|
152
|
+
available: available,
|
|
153
|
+
pending: pending,
|
|
154
|
+
currency: 'USD',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
throw new Error(`Mercury getBalance failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// ── Helpers ──────────────────────────────────────────
|
|
163
|
+
function mapMercuryKind(kind, amount) {
|
|
164
|
+
// Mercury transaction kinds: externalTransfer, internalTransfer, outgoingPayment, incomingPayment, etc.
|
|
165
|
+
// Map to revenue-source-adapter types based on direction (amount sign)
|
|
166
|
+
if (kind === 'incomingPayment' || (amount !== undefined && amount > 0))
|
|
167
|
+
return 'charge';
|
|
168
|
+
if (kind === 'outgoingPayment' || (amount !== undefined && amount < 0))
|
|
169
|
+
return 'refund';
|
|
170
|
+
return 'charge';
|
|
171
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Stablecoin Adapter — full implementation for development/demo.
|
|
3
|
+
*
|
|
4
|
+
* Returns realistic fake data matching the StablecoinSetup + StablecoinAdapter
|
|
5
|
+
* interfaces. Every method returns valid-shaped data. No throws.
|
|
6
|
+
* This IS a full implementation for a sandbox provider (No Stubs Doctrine).
|
|
7
|
+
*
|
|
8
|
+
* Simulates a USDC wallet with ~$50,000 starting balance, realistic off-ramp
|
|
9
|
+
* lifecycle (pending -> processing -> completed over 3 polls), and deterministic
|
|
10
|
+
* fee estimates.
|
|
11
|
+
*/
|
|
12
|
+
import type { StablecoinSetup, StablecoinAdapter, ProviderCredentials, SupportedAsset, StablecoinBalance, CombinedBalances, OfframpQuote, TransferRecord, TransferStatusDetail, FundingPlanRef, DateRange } from './base.js';
|
|
13
|
+
type Cents = number & {
|
|
14
|
+
readonly __brand: 'Cents';
|
|
15
|
+
};
|
|
16
|
+
export declare class SandboxStablecoinSetup implements StablecoinSetup {
|
|
17
|
+
authenticate(_credentials: ProviderCredentials): Promise<{
|
|
18
|
+
valid: boolean;
|
|
19
|
+
accountId?: string;
|
|
20
|
+
permissions?: string[];
|
|
21
|
+
error?: string;
|
|
22
|
+
}>;
|
|
23
|
+
verifySupportedAssets(_credentials: ProviderCredentials): Promise<SupportedAsset[]>;
|
|
24
|
+
verifyLinkedBank(_credentials: ProviderCredentials): Promise<{
|
|
25
|
+
linked: boolean;
|
|
26
|
+
bankId?: string;
|
|
27
|
+
bankName?: string;
|
|
28
|
+
accountLast4?: string;
|
|
29
|
+
error?: string;
|
|
30
|
+
}>;
|
|
31
|
+
getInitialBalances(_credentials: ProviderCredentials): Promise<StablecoinBalance[]>;
|
|
32
|
+
}
|
|
33
|
+
export declare class SandboxStablecoinAdapter implements StablecoinAdapter {
|
|
34
|
+
private balanceCents;
|
|
35
|
+
private transfers;
|
|
36
|
+
constructor(initialBalanceDollars?: number);
|
|
37
|
+
getBalances(): Promise<CombinedBalances>;
|
|
38
|
+
quoteRedemption(amountCents: Cents): Promise<OfframpQuote>;
|
|
39
|
+
initiateOfframp(plan: FundingPlanRef, previousHash: string): Promise<TransferRecord>;
|
|
40
|
+
getTransferStatus(transferId: string): Promise<TransferStatusDetail>;
|
|
41
|
+
cancelTransfer(transferId: string): Promise<{
|
|
42
|
+
cancelled: boolean;
|
|
43
|
+
reason?: string;
|
|
44
|
+
}>;
|
|
45
|
+
listCompletedTransfers(_dateRange: DateRange): Promise<TransferRecord[]>;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Stablecoin Adapter — full implementation for development/demo.
|
|
3
|
+
*
|
|
4
|
+
* Returns realistic fake data matching the StablecoinSetup + StablecoinAdapter
|
|
5
|
+
* interfaces. Every method returns valid-shaped data. No throws.
|
|
6
|
+
* This IS a full implementation for a sandbox provider (No Stubs Doctrine).
|
|
7
|
+
*
|
|
8
|
+
* Simulates a USDC wallet with ~$50,000 starting balance, realistic off-ramp
|
|
9
|
+
* lifecycle (pending -> processing -> completed over 3 polls), and deterministic
|
|
10
|
+
* fee estimates.
|
|
11
|
+
*/
|
|
12
|
+
import { randomUUID } from 'node:crypto';
|
|
13
|
+
import { toCents, computeTransferHash } from './base.js';
|
|
14
|
+
// ── Setup Implementation ─────────────────────────────
|
|
15
|
+
export class SandboxStablecoinSetup {
|
|
16
|
+
async authenticate(_credentials) {
|
|
17
|
+
return {
|
|
18
|
+
valid: true,
|
|
19
|
+
accountId: `sandbox_wallet_${randomUUID().slice(0, 8)}`,
|
|
20
|
+
permissions: ['read_balances', 'initiate_payouts', 'read_payouts'],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async verifySupportedAssets(_credentials) {
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
asset: 'USDC',
|
|
27
|
+
network: 'ETH',
|
|
28
|
+
contractAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
|
29
|
+
minRedemption: toCents(100),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
asset: 'USDC',
|
|
33
|
+
network: 'SOL',
|
|
34
|
+
contractAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
35
|
+
minRedemption: toCents(50),
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
async verifyLinkedBank(_credentials) {
|
|
40
|
+
return {
|
|
41
|
+
linked: true,
|
|
42
|
+
bankId: 'sandbox_bank_001',
|
|
43
|
+
bankName: 'Sandbox Mercury Account',
|
|
44
|
+
accountLast4: '4242',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async getInitialBalances(_credentials) {
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
provider: 'circle',
|
|
51
|
+
asset: 'USDC',
|
|
52
|
+
network: 'ETH',
|
|
53
|
+
balanceCents: toCents(50_000),
|
|
54
|
+
lastUpdated: new Date().toISOString(),
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// ── Runtime Adapter Implementation ───────────────────
|
|
60
|
+
export class SandboxStablecoinAdapter {
|
|
61
|
+
balanceCents;
|
|
62
|
+
transfers = new Map();
|
|
63
|
+
constructor(initialBalanceDollars = 50_000) {
|
|
64
|
+
this.balanceCents = toCents(initialBalanceDollars);
|
|
65
|
+
}
|
|
66
|
+
async getBalances() {
|
|
67
|
+
const now = new Date().toISOString();
|
|
68
|
+
const stablecoin = [
|
|
69
|
+
{
|
|
70
|
+
provider: 'circle',
|
|
71
|
+
asset: 'USDC',
|
|
72
|
+
network: 'ETH',
|
|
73
|
+
balanceCents: this.balanceCents,
|
|
74
|
+
lastUpdated: now,
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
return {
|
|
78
|
+
stablecoin,
|
|
79
|
+
fiat: [],
|
|
80
|
+
totalStablecoinCents: this.balanceCents,
|
|
81
|
+
totalFiatAvailableCents: 0,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
async quoteRedemption(amountCents) {
|
|
85
|
+
// Sandbox fee: flat $10 per off-ramp
|
|
86
|
+
const feeCents = toCents(10);
|
|
87
|
+
return {
|
|
88
|
+
provider: 'circle',
|
|
89
|
+
sourceAsset: 'USDC',
|
|
90
|
+
sourceNetwork: 'ETH',
|
|
91
|
+
requestedCents: amountCents,
|
|
92
|
+
estimatedFeeCents: feeCents,
|
|
93
|
+
estimatedNetCents: (amountCents - feeCents),
|
|
94
|
+
estimatedSettlementMinutes: 60,
|
|
95
|
+
expiresAt: new Date(Date.now() + 10 * 60 * 1000).toISOString(),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async initiateOfframp(plan, previousHash) {
|
|
99
|
+
const feeCents = toCents(10);
|
|
100
|
+
const netCents = (plan.requiredCents - feeCents);
|
|
101
|
+
const now = new Date().toISOString();
|
|
102
|
+
const id = randomUUID();
|
|
103
|
+
const providerTransferId = `sandbox_xfer_${randomUUID().slice(0, 12)}`;
|
|
104
|
+
// Deduct from simulated balance
|
|
105
|
+
this.balanceCents = (this.balanceCents - plan.requiredCents);
|
|
106
|
+
const record = {
|
|
107
|
+
id,
|
|
108
|
+
fundingPlanId: plan.id,
|
|
109
|
+
providerTransferId,
|
|
110
|
+
provider: 'circle',
|
|
111
|
+
direction: 'crypto_to_fiat',
|
|
112
|
+
sourceAsset: 'USDC',
|
|
113
|
+
sourceNetwork: 'ETH',
|
|
114
|
+
amountCents: plan.requiredCents,
|
|
115
|
+
feesCents: feeCents,
|
|
116
|
+
netAmountCents: netCents,
|
|
117
|
+
destinationBankId: plan.destinationBankId,
|
|
118
|
+
status: 'pending',
|
|
119
|
+
initiatedAt: now,
|
|
120
|
+
idempotencyKey: plan.idempotencyKey,
|
|
121
|
+
previousHash,
|
|
122
|
+
};
|
|
123
|
+
const hash = computeTransferHash(record, previousHash);
|
|
124
|
+
const fullRecord = { ...record, hash };
|
|
125
|
+
this.transfers.set(id, { record: fullRecord, pollCount: 0 });
|
|
126
|
+
return fullRecord;
|
|
127
|
+
}
|
|
128
|
+
async getTransferStatus(transferId) {
|
|
129
|
+
const state = this.transfers.get(transferId);
|
|
130
|
+
if (!state) {
|
|
131
|
+
// Return failed status for unknown IDs — unknown transfers should not
|
|
132
|
+
// be reported as completed (could cause false settlement in reconciliation).
|
|
133
|
+
return {
|
|
134
|
+
transferId,
|
|
135
|
+
providerTransferId: 'unknown',
|
|
136
|
+
status: 'failed',
|
|
137
|
+
amountCents: 0,
|
|
138
|
+
feesCents: 0,
|
|
139
|
+
initiatedAt: new Date().toISOString(),
|
|
140
|
+
providerRawStatus: 'not_found',
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// Simulate lifecycle: pending (poll 0) -> processing (poll 1) -> completed (poll 2+)
|
|
144
|
+
state.pollCount += 1;
|
|
145
|
+
let status;
|
|
146
|
+
let rawStatus;
|
|
147
|
+
let completedAt;
|
|
148
|
+
if (state.pollCount <= 1) {
|
|
149
|
+
status = 'pending';
|
|
150
|
+
rawStatus = 'pending';
|
|
151
|
+
}
|
|
152
|
+
else if (state.pollCount === 2) {
|
|
153
|
+
status = 'processing';
|
|
154
|
+
rawStatus = 'processing';
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
status = 'completed';
|
|
158
|
+
rawStatus = 'complete';
|
|
159
|
+
completedAt = new Date().toISOString();
|
|
160
|
+
// Update stored record status
|
|
161
|
+
state.record = { ...state.record, status: 'completed', completedAt };
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
transferId: state.record.id,
|
|
165
|
+
providerTransferId: state.record.providerTransferId,
|
|
166
|
+
status,
|
|
167
|
+
amountCents: state.record.amountCents,
|
|
168
|
+
feesCents: state.record.feesCents,
|
|
169
|
+
initiatedAt: state.record.initiatedAt,
|
|
170
|
+
completedAt,
|
|
171
|
+
estimatedCompletionAt: status !== 'completed'
|
|
172
|
+
? new Date(Date.now() + 60 * 60 * 1000).toISOString()
|
|
173
|
+
: undefined,
|
|
174
|
+
providerRawStatus: rawStatus,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
async cancelTransfer(transferId) {
|
|
178
|
+
const state = this.transfers.get(transferId);
|
|
179
|
+
if (!state) {
|
|
180
|
+
return { cancelled: false, reason: 'Transfer not found' };
|
|
181
|
+
}
|
|
182
|
+
if (state.record.status === 'completed') {
|
|
183
|
+
return { cancelled: false, reason: 'Transfer already completed' };
|
|
184
|
+
}
|
|
185
|
+
if (state.record.status === 'processing') {
|
|
186
|
+
return { cancelled: false, reason: 'Transfer is processing — cannot cancel' };
|
|
187
|
+
}
|
|
188
|
+
// Refund the balance and mark cancelled
|
|
189
|
+
this.balanceCents = (this.balanceCents + state.record.amountCents);
|
|
190
|
+
state.record = { ...state.record, status: 'cancelled' };
|
|
191
|
+
return { cancelled: true };
|
|
192
|
+
}
|
|
193
|
+
async listCompletedTransfers(_dateRange) {
|
|
194
|
+
const completed = [];
|
|
195
|
+
for (const state of this.transfers.values()) {
|
|
196
|
+
if (state.record.status === 'completed') {
|
|
197
|
+
completed.push(state.record);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return completed;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Treasury Planner — pure logic module for runway forecasting and plan generation.
|
|
3
|
+
*
|
|
4
|
+
* No API calls, no file I/O — fully testable deterministic functions.
|
|
5
|
+
* All monetary values use branded integer cents (Cents type).
|
|
6
|
+
*
|
|
7
|
+
* PRD Reference: §12.4 (FundingPlan), §12.5 (TransferRecord), §15 (Rules Engine)
|
|
8
|
+
* Agents: Dockson (treasury), Heartbeat daemon
|
|
9
|
+
*/
|
|
10
|
+
import type { Cents, FundingPlan, OperatingBankAccount, StablecoinFundingSource, PendingObligation } from '../patterns/funding-plan.js';
|
|
11
|
+
interface CampaignSpendProjection {
|
|
12
|
+
campaignId: string;
|
|
13
|
+
platform: 'google' | 'meta';
|
|
14
|
+
dailyBudgetCents: Cents;
|
|
15
|
+
status: 'active' | 'paused';
|
|
16
|
+
}
|
|
17
|
+
interface FundingPlanConfig {
|
|
18
|
+
/** Minimum off-ramp amount (provider-enforced) */
|
|
19
|
+
minimumOfframpCents: Cents;
|
|
20
|
+
/** Target buffer above minimum — the "comfort zone" */
|
|
21
|
+
bufferTargetCents: Cents;
|
|
22
|
+
/** Maximum single off-ramp amount per day */
|
|
23
|
+
maxDailyOfframpCents: Cents;
|
|
24
|
+
/** Days of runway to maintain as target */
|
|
25
|
+
targetRunwayDays: number;
|
|
26
|
+
}
|
|
27
|
+
interface OfframpDecision {
|
|
28
|
+
shouldOfframp: boolean;
|
|
29
|
+
reason: string;
|
|
30
|
+
amountCents: Cents;
|
|
31
|
+
}
|
|
32
|
+
interface ObligationInput {
|
|
33
|
+
id: string;
|
|
34
|
+
platform: 'google' | 'meta';
|
|
35
|
+
type: 'invoice' | 'debit' | 'buffer';
|
|
36
|
+
amountCents: Cents;
|
|
37
|
+
dueDate: string;
|
|
38
|
+
overdue: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare function calculateRunway(bankBalanceCents: Cents, dailySpendRateCents: Cents): number;
|
|
41
|
+
export declare function forecastSpend(campaigns: CampaignSpendProjection[], days: number): Cents;
|
|
42
|
+
export declare function shouldTriggerOfframp(bankBalanceCents: Cents, bufferThresholdCents: Cents, pendingSpendCents: Cents): OfframpDecision;
|
|
43
|
+
export declare function calculateRequiredOfframp(bankBalanceCents: Cents, bufferTargetCents: Cents, pendingObligationsCents: Cents, feeEstimateCents: Cents): Cents;
|
|
44
|
+
export declare function prioritizeAllObligations(invoices: ObligationInput[], debits: ObligationInput[], bufferNeeds: ObligationInput[]): PendingObligation[];
|
|
45
|
+
export declare function generateFundingPlan(source: StablecoinFundingSource, bank: OperatingBankAccount, obligations: ObligationInput[], config: FundingPlanConfig, previousHash: string): FundingPlan | null;
|
|
46
|
+
export declare function calculateDailySpendRate(campaigns: CampaignSpendProjection[]): Cents;
|
|
47
|
+
export declare function forecastRunway(bankBalanceCents: Cents, campaigns: CampaignSpendProjection[], pendingObligationsCents: Cents): {
|
|
48
|
+
runwayDays: number;
|
|
49
|
+
dailySpendCents: Cents;
|
|
50
|
+
effectiveBalanceCents: Cents;
|
|
51
|
+
};
|
|
52
|
+
export type { CampaignSpendProjection, FundingPlanConfig, OfframpDecision, ObligationInput, };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Treasury Planner — pure logic module for runway forecasting and plan generation.
|
|
3
|
+
*
|
|
4
|
+
* No API calls, no file I/O — fully testable deterministic functions.
|
|
5
|
+
* All monetary values use branded integer cents (Cents type).
|
|
6
|
+
*
|
|
7
|
+
* PRD Reference: §12.4 (FundingPlan), §12.5 (TransferRecord), §15 (Rules Engine)
|
|
8
|
+
* Agents: Dockson (treasury), Heartbeat daemon
|
|
9
|
+
*/
|
|
10
|
+
import { toCents, createFundingPlan, prioritizeObligations, } from '../patterns/funding-plan.js';
|
|
11
|
+
// ── Runway Calculation ───────────────────────────────
|
|
12
|
+
// Returns the number of days the current bank balance can sustain spend.
|
|
13
|
+
export function calculateRunway(bankBalanceCents, dailySpendRateCents) {
|
|
14
|
+
if (dailySpendRateCents <= 0)
|
|
15
|
+
return Infinity;
|
|
16
|
+
return Math.floor(bankBalanceCents / dailySpendRateCents);
|
|
17
|
+
}
|
|
18
|
+
// ── Spend Forecast ───────────────────────────────────
|
|
19
|
+
// Projects total spend over N days based on active campaign budgets.
|
|
20
|
+
export function forecastSpend(campaigns, days) {
|
|
21
|
+
if (days <= 0)
|
|
22
|
+
return 0;
|
|
23
|
+
const dailyTotal = campaigns
|
|
24
|
+
.filter(c => c.status === 'active')
|
|
25
|
+
.reduce((sum, c) => sum + c.dailyBudgetCents, 0);
|
|
26
|
+
return Math.round(dailyTotal * days);
|
|
27
|
+
}
|
|
28
|
+
// ── Off-ramp Trigger Decision ────────────────────────
|
|
29
|
+
// Determines whether an off-ramp should be triggered and why.
|
|
30
|
+
export function shouldTriggerOfframp(bankBalanceCents, bufferThresholdCents, pendingSpendCents) {
|
|
31
|
+
const effectiveBalance = (bankBalanceCents - pendingSpendCents);
|
|
32
|
+
if (effectiveBalance < bufferThresholdCents) {
|
|
33
|
+
const deficit = (bufferThresholdCents - effectiveBalance);
|
|
34
|
+
return {
|
|
35
|
+
shouldOfframp: true,
|
|
36
|
+
reason: `Effective balance $${(effectiveBalance / 100).toFixed(2)} below buffer threshold $${(bufferThresholdCents / 100).toFixed(2)} — deficit $${(deficit / 100).toFixed(2)}`,
|
|
37
|
+
amountCents: deficit,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
shouldOfframp: false,
|
|
42
|
+
reason: `Effective balance $${(effectiveBalance / 100).toFixed(2)} above buffer threshold $${(bufferThresholdCents / 100).toFixed(2)}`,
|
|
43
|
+
amountCents: 0,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// ── Required Off-ramp Amount ─────────────────────────
|
|
47
|
+
// Calculates the exact off-ramp amount needed, clamped to provider constraints.
|
|
48
|
+
export function calculateRequiredOfframp(bankBalanceCents, bufferTargetCents, pendingObligationsCents, feeEstimateCents) {
|
|
49
|
+
// How much fiat we need: buffer + obligations + fees - current balance
|
|
50
|
+
const required = (bufferTargetCents + pendingObligationsCents + feeEstimateCents - bankBalanceCents);
|
|
51
|
+
if (required <= 0)
|
|
52
|
+
return 0;
|
|
53
|
+
return required;
|
|
54
|
+
}
|
|
55
|
+
// ── Obligation Prioritization ────────────────────────
|
|
56
|
+
// Sorts obligations by urgency: overdue first, then by due date.
|
|
57
|
+
export function prioritizeAllObligations(invoices, debits, bufferNeeds) {
|
|
58
|
+
const all = [...invoices, ...debits, ...bufferNeeds].map(o => ({
|
|
59
|
+
id: o.id,
|
|
60
|
+
platform: o.platform,
|
|
61
|
+
amountCents: o.amountCents,
|
|
62
|
+
dueDate: o.dueDate,
|
|
63
|
+
overdue: o.overdue,
|
|
64
|
+
}));
|
|
65
|
+
return prioritizeObligations(all);
|
|
66
|
+
}
|
|
67
|
+
// ── Funding Plan Generation ──────────────────────────
|
|
68
|
+
// Creates a FundingPlan based on current treasury state and obligations.
|
|
69
|
+
export function generateFundingPlan(source, bank, obligations, config, previousHash) {
|
|
70
|
+
// Sum all pending obligation amounts
|
|
71
|
+
const totalObligationsCents = obligations.reduce((sum, o) => sum + o.amountCents, 0);
|
|
72
|
+
// Calculate how much additional fiat we need
|
|
73
|
+
const requiredCents = calculateRequiredOfframp(bank.availableBalanceCents, config.bufferTargetCents, totalObligationsCents, toCents(10));
|
|
74
|
+
// No funding needed
|
|
75
|
+
if (requiredCents <= 0)
|
|
76
|
+
return null;
|
|
77
|
+
// Clamp to maximum daily off-ramp
|
|
78
|
+
const clampedCents = Math.min(requiredCents, config.maxDailyOfframpCents);
|
|
79
|
+
// Enforce minimum off-ramp amount
|
|
80
|
+
if (clampedCents < config.minimumOfframpCents) {
|
|
81
|
+
// If the required amount is below provider minimum but > 0, round up to minimum
|
|
82
|
+
const finalCents = config.minimumOfframpCents;
|
|
83
|
+
return createFundingPlan(determineFundingReason(obligations, bank), source.id, bank.id, determinePlatformTarget(obligations), finalCents, previousHash);
|
|
84
|
+
}
|
|
85
|
+
return createFundingPlan(determineFundingReason(obligations, bank), source.id, bank.id, determinePlatformTarget(obligations), clampedCents, previousHash);
|
|
86
|
+
}
|
|
87
|
+
// ── Daily Spend Rate ─────────────────────────────────
|
|
88
|
+
// Calculates the average daily spend rate from active campaigns.
|
|
89
|
+
export function calculateDailySpendRate(campaigns) {
|
|
90
|
+
return campaigns
|
|
91
|
+
.filter(c => c.status === 'active')
|
|
92
|
+
.reduce((sum, c) => (sum + c.dailyBudgetCents), 0);
|
|
93
|
+
}
|
|
94
|
+
// ── Runway Forecast ──────────────────────────────────
|
|
95
|
+
// Combines balance, spend rate, and pending obligations into a runway projection.
|
|
96
|
+
export function forecastRunway(bankBalanceCents, campaigns, pendingObligationsCents) {
|
|
97
|
+
const dailySpendCents = calculateDailySpendRate(campaigns);
|
|
98
|
+
const effectiveBalanceCents = (bankBalanceCents - pendingObligationsCents);
|
|
99
|
+
const runwayDays = calculateRunway(Math.max(effectiveBalanceCents, 0), dailySpendCents);
|
|
100
|
+
return { runwayDays, dailySpendCents, effectiveBalanceCents };
|
|
101
|
+
}
|
|
102
|
+
// ── Private Helpers ──────────────────────────────────
|
|
103
|
+
function determineFundingReason(obligations, bank) {
|
|
104
|
+
// Check for overdue invoices first
|
|
105
|
+
const hasOverdue = obligations.some(o => o.overdue);
|
|
106
|
+
if (hasOverdue)
|
|
107
|
+
return 'INVOICE_DUE';
|
|
108
|
+
// Check for upcoming invoices within 7 days
|
|
109
|
+
const sevenDaysOut = Date.now() + 7 * 24 * 60 * 60 * 1000;
|
|
110
|
+
const hasUpcoming = obligations.some(o => o.type === 'invoice' && new Date(o.dueDate).getTime() <= sevenDaysOut);
|
|
111
|
+
if (hasUpcoming)
|
|
112
|
+
return 'INVOICE_DUE';
|
|
113
|
+
// Check if bank is below minimum buffer
|
|
114
|
+
if (bank.availableBalanceCents < bank.minimumBufferCents)
|
|
115
|
+
return 'LOW_BUFFER';
|
|
116
|
+
// Default: runway shortfall
|
|
117
|
+
return 'RUNWAY_SHORTFALL';
|
|
118
|
+
}
|
|
119
|
+
function determinePlatformTarget(obligations) {
|
|
120
|
+
// If all obligations are for one platform, target that platform
|
|
121
|
+
const platforms = new Set(obligations.map(o => o.platform));
|
|
122
|
+
if (platforms.size === 1) {
|
|
123
|
+
const platform = [...platforms][0];
|
|
124
|
+
return platform;
|
|
125
|
+
}
|
|
126
|
+
// Mixed or no obligations: shared buffer
|
|
127
|
+
return 'shared_buffer';
|
|
128
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Financial core — re-exports from the financial-transaction pattern for wizard runtime use.
|
|
3
|
+
* ARCH-R2-012: Production code should not import from docs/patterns/ directly.
|
|
4
|
+
*/
|
|
5
|
+
export { atomicWrite, appendToLog, idempotentAppend, toCents, toDollars, formatCurrency, formatRoas, formatPercentage, SPEND_LOG, REVENUE_LOG, TREASURY_DIR } from './patterns/financial-transaction.js';
|
|
6
|
+
export type { Cents, ReconciliationReport, Transaction, Budget, GrowthCampaign, RevenueEvent } from './patterns/financial-transaction.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Financial core — re-exports from the financial-transaction pattern for wizard runtime use.
|
|
3
|
+
* ARCH-R2-012: Production code should not import from docs/patterns/ directly.
|
|
4
|
+
*/
|
|
5
|
+
export { atomicWrite, appendToLog, idempotentAppend, toCents, toDollars, formatCurrency, formatRoas, formatPercentage, SPEND_LOG, REVENUE_LOG, TREASURY_DIR } from './patterns/financial-transaction.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
/** Store a financial credential */
|
|
18
|
+
export declare function financialVaultSet(password: string, key: string, value: string): Promise<void>;
|
|
19
|
+
/** Retrieve a financial credential */
|
|
20
|
+
export declare function financialVaultGet(password: string, key: string): Promise<string | null>;
|
|
21
|
+
/** Delete a financial credential */
|
|
22
|
+
export declare function financialVaultDelete(password: string, key: string): Promise<void>;
|
|
23
|
+
/** Check if the financial vault exists */
|
|
24
|
+
export declare function financialVaultExists(): boolean;
|
|
25
|
+
/** Verify password can decrypt the vault */
|
|
26
|
+
export declare function financialVaultUnlock(password: string): Promise<boolean>;
|
|
27
|
+
/** List stored keys (requires password) */
|
|
28
|
+
export declare function financialVaultKeys(password: string): Promise<string[]>;
|
|
29
|
+
/** Zero the in-memory cache — call on SIGTERM, vault timeout, or manual lock */
|
|
30
|
+
export declare function financialVaultLock(): void;
|
|
31
|
+
/** Return the vault file path */
|
|
32
|
+
export declare function financialVaultPath(): string;
|
|
33
|
+
/** Return the treasury directory path */
|
|
34
|
+
export declare function treasuryDir(): string;
|