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,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kongo Pages — Page lifecycle operations.
|
|
3
|
+
*
|
|
4
|
+
* Generate landing pages from PRD seed content, poll for completion,
|
|
5
|
+
* retrieve HTML, list/delete pages, and batch generate.
|
|
6
|
+
*
|
|
7
|
+
* Uses the real Kongo API surface:
|
|
8
|
+
* - POST /engine/pages — generate a page (accepts brief + template for structured input)
|
|
9
|
+
* - GET /engine/pages/:pageId — poll status until READY
|
|
10
|
+
* - GET /engine/pages/:pageId/html — get raw HTML
|
|
11
|
+
* - GET /engine/pages — list pages (cursor pagination)
|
|
12
|
+
* - DELETE /engine/pages/:pageId — soft delete
|
|
13
|
+
* - POST /engine/pages/batch — batch generate 1-50 pages
|
|
14
|
+
*
|
|
15
|
+
* PRD Reference: PRD-kongo-integration.md §4.1
|
|
16
|
+
*/
|
|
17
|
+
import { randomUUID } from 'node:crypto';
|
|
18
|
+
import { KongoApiError } from './client.js';
|
|
19
|
+
// ── Page Operations ──────────────────────────────────────
|
|
20
|
+
const DEFAULT_POLL_INTERVAL_MS = 3_000;
|
|
21
|
+
// Kongo page generation takes 2-10 minutes. Default timeout covers the full range.
|
|
22
|
+
const DEFAULT_POLL_TIMEOUT_MS = 660_000; // 11 minutes (10 min upper bound + 1 min buffer)
|
|
23
|
+
/**
|
|
24
|
+
* Create a page from raw configuration.
|
|
25
|
+
* Returns immediately with pageId — generation runs async (2-10 min).
|
|
26
|
+
*/
|
|
27
|
+
export async function createPage(client, config) {
|
|
28
|
+
return client.post('/engine/pages', config, { 'Idempotency-Key': randomUUID() });
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create a landing page from PRD seed content.
|
|
32
|
+
* Maps PrdSeedContent to Kongo's CreatePageRequest with brief + template: 'landing-page'.
|
|
33
|
+
*/
|
|
34
|
+
export async function createPageFromPrd(client, seed) {
|
|
35
|
+
const brief = {
|
|
36
|
+
companyName: seed.projectName,
|
|
37
|
+
headline: seed.headline,
|
|
38
|
+
subheadline: seed.subheadline,
|
|
39
|
+
valuePropositions: seed.valueProps,
|
|
40
|
+
ctaText: seed.ctaText,
|
|
41
|
+
ctaUrl: seed.ctaUrl,
|
|
42
|
+
};
|
|
43
|
+
if (seed.logoUrl)
|
|
44
|
+
brief.logoUrl = seed.logoUrl;
|
|
45
|
+
if (seed.socialProof?.length)
|
|
46
|
+
brief.socialProof = seed.socialProof;
|
|
47
|
+
const metadata = {
|
|
48
|
+
source: 'voidforge',
|
|
49
|
+
projectName: seed.projectName,
|
|
50
|
+
};
|
|
51
|
+
if (seed.campaignId)
|
|
52
|
+
metadata.campaignId = seed.campaignId;
|
|
53
|
+
if (seed.platform)
|
|
54
|
+
metadata.platform = seed.platform;
|
|
55
|
+
return createPage(client, {
|
|
56
|
+
companyName: seed.projectName,
|
|
57
|
+
content: [
|
|
58
|
+
seed.headline,
|
|
59
|
+
seed.subheadline,
|
|
60
|
+
...seed.valueProps,
|
|
61
|
+
seed.ctaText,
|
|
62
|
+
...(seed.socialProof ?? []),
|
|
63
|
+
].join('\n\n'),
|
|
64
|
+
brief,
|
|
65
|
+
template: 'landing-page',
|
|
66
|
+
style: {
|
|
67
|
+
colors: seed.brandColors,
|
|
68
|
+
},
|
|
69
|
+
hosted: true,
|
|
70
|
+
metadata,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the current status and details of a page.
|
|
75
|
+
*/
|
|
76
|
+
export async function getPageStatus(client, pageId) {
|
|
77
|
+
return client.get(`/engine/pages/${encodeURIComponent(pageId)}`);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get the raw HTML of a generated page.
|
|
81
|
+
* Only works when status is READY.
|
|
82
|
+
*/
|
|
83
|
+
export async function getPageHtml(client, pageId, options) {
|
|
84
|
+
const query = {};
|
|
85
|
+
if (options?.tracking !== undefined)
|
|
86
|
+
query.tracking = String(options.tracking);
|
|
87
|
+
if (options?.minify !== undefined)
|
|
88
|
+
query.minify = String(options.minify);
|
|
89
|
+
return client.get(`/engine/pages/${encodeURIComponent(pageId)}/html`, query);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Poll a page until it reaches READY status or timeout.
|
|
93
|
+
* Throws on ERROR status or timeout.
|
|
94
|
+
*/
|
|
95
|
+
export async function awaitPage(client, pageId, options) {
|
|
96
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
|
|
97
|
+
const intervalMs = options?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
98
|
+
const deadline = Date.now() + timeoutMs;
|
|
99
|
+
while (Date.now() < deadline) {
|
|
100
|
+
const page = await getPageStatus(client, pageId);
|
|
101
|
+
if (page.status === 'READY')
|
|
102
|
+
return page;
|
|
103
|
+
if (page.status === 'ERROR') {
|
|
104
|
+
throw new KongoApiError('GENERATION_FAILED', `Page generation failed: ${page.error?.message ?? 'Unknown error'}`, 500);
|
|
105
|
+
}
|
|
106
|
+
// Still GENERATING — wait and retry
|
|
107
|
+
const remaining = deadline - Date.now();
|
|
108
|
+
if (remaining <= 0)
|
|
109
|
+
break;
|
|
110
|
+
await sleep(Math.min(intervalMs, remaining));
|
|
111
|
+
}
|
|
112
|
+
throw new KongoApiError('TIMEOUT', `Page ${pageId} generation timed out after ${timeoutMs / 1000}s`, 408, true);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* List pages with cursor-based pagination.
|
|
116
|
+
*/
|
|
117
|
+
export async function listPages(client, options) {
|
|
118
|
+
const query = {};
|
|
119
|
+
if (options?.cursor)
|
|
120
|
+
query.cursor = options.cursor;
|
|
121
|
+
if (options?.limit)
|
|
122
|
+
query.limit = options.limit;
|
|
123
|
+
if (options?.status)
|
|
124
|
+
query.status = options.status;
|
|
125
|
+
if (options?.sort)
|
|
126
|
+
query.sort = options.sort;
|
|
127
|
+
if (options?.order)
|
|
128
|
+
query.order = options.order;
|
|
129
|
+
return client.get('/engine/pages', query);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Soft-delete a page. HTML is purged immediately, metadata retained 90 days.
|
|
133
|
+
*/
|
|
134
|
+
export async function deletePage(client, pageId) {
|
|
135
|
+
await client.delete(`/engine/pages/${encodeURIComponent(pageId)}`);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Batch generate 1-50 pages in a single request.
|
|
139
|
+
*/
|
|
140
|
+
export async function batchGenerate(client, configs) {
|
|
141
|
+
if (configs.length === 0 || configs.length > 50) {
|
|
142
|
+
throw new KongoApiError('VALIDATION_ERROR', `Batch size must be 1-50, got ${configs.length}`, 400);
|
|
143
|
+
}
|
|
144
|
+
const result = await client.post('/engine/pages/batch', { pages: configs }, { 'Idempotency-Key': randomUUID() });
|
|
145
|
+
return result.pages;
|
|
146
|
+
}
|
|
147
|
+
// ── Helpers ──────────────────────────────────────────────
|
|
148
|
+
function sleep(ms) {
|
|
149
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
150
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kongo Provisioner — API key setup and connection verification.
|
|
3
|
+
*
|
|
4
|
+
* Called during /cultivation install Step 2b: "Connect Kongo for landing pages?"
|
|
5
|
+
*
|
|
6
|
+
* Kongo uses API keys (ke_live_ prefix), not OAuth. Keys are created in the
|
|
7
|
+
* Kongo dashboard at kongo.io/dashboard/api. The provisioner:
|
|
8
|
+
* 1. Accepts a user-provided API key
|
|
9
|
+
* 2. Verifies the connection with GET /engine/pages (expects 200)
|
|
10
|
+
* 3. Stores the key in the financial vault
|
|
11
|
+
* 4. Stores the webhook signing secret (optional)
|
|
12
|
+
*
|
|
13
|
+
* PRD Reference: PRD-kongo-integration.md §4.4 (adjusted from OAuth to manual)
|
|
14
|
+
*/
|
|
15
|
+
import { KongoClient } from './client.js';
|
|
16
|
+
/** Vault key for the Kongo API key */
|
|
17
|
+
export declare const KONGO_API_KEY_VAULT_KEY = "kongo-api-key";
|
|
18
|
+
/** Vault key for the webhook signing secret */
|
|
19
|
+
export declare const KONGO_WEBHOOK_SECRET_VAULT_KEY = "kongo-webhook-secret";
|
|
20
|
+
export interface KongoConnectionStatus {
|
|
21
|
+
readonly connected: boolean;
|
|
22
|
+
readonly pageCount?: number;
|
|
23
|
+
readonly error?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Verify a Kongo API key by making a read-only API call.
|
|
27
|
+
* Returns connection status with page count on success.
|
|
28
|
+
*/
|
|
29
|
+
export declare function verifyKongoConnection(apiKey: string): Promise<KongoConnectionStatus>;
|
|
30
|
+
export interface KongoProvisionConfig {
|
|
31
|
+
readonly apiKey: string;
|
|
32
|
+
readonly webhookSecret?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface KongoProvisionResult {
|
|
35
|
+
readonly success: boolean;
|
|
36
|
+
readonly message: string;
|
|
37
|
+
readonly connection?: KongoConnectionStatus;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Provision Kongo access during /cultivation install.
|
|
41
|
+
*
|
|
42
|
+
* Steps:
|
|
43
|
+
* 1. Validate key format (ke_live_ prefix)
|
|
44
|
+
* 2. Verify connection with read-only API call
|
|
45
|
+
* 3. Store key in financial vault
|
|
46
|
+
* 4. Optionally store webhook signing secret
|
|
47
|
+
*
|
|
48
|
+
* @param config - API key and optional webhook secret
|
|
49
|
+
* @param vaultSet - Financial vault setter (injected for testability)
|
|
50
|
+
*/
|
|
51
|
+
export declare function provisionKongo(config: KongoProvisionConfig, vaultSet: (key: string, value: string) => Promise<void>): Promise<KongoProvisionResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Check if Kongo is connected (API key exists in vault).
|
|
54
|
+
*/
|
|
55
|
+
export declare function isKongoConnected(vaultGet: (key: string) => Promise<string | null>): Promise<boolean>;
|
|
56
|
+
/**
|
|
57
|
+
* Create a KongoClient from vault credentials.
|
|
58
|
+
* Returns null if Kongo is not connected.
|
|
59
|
+
*/
|
|
60
|
+
export declare function getKongoClient(vaultGet: (key: string) => Promise<string | null>): Promise<KongoClient | null>;
|
|
61
|
+
/**
|
|
62
|
+
* Remove Kongo credentials from vault (disconnect).
|
|
63
|
+
*/
|
|
64
|
+
export declare function disconnectKongo(vaultDelete: (key: string) => Promise<void>): Promise<void>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kongo Provisioner — API key setup and connection verification.
|
|
3
|
+
*
|
|
4
|
+
* Called during /cultivation install Step 2b: "Connect Kongo for landing pages?"
|
|
5
|
+
*
|
|
6
|
+
* Kongo uses API keys (ke_live_ prefix), not OAuth. Keys are created in the
|
|
7
|
+
* Kongo dashboard at kongo.io/dashboard/api. The provisioner:
|
|
8
|
+
* 1. Accepts a user-provided API key
|
|
9
|
+
* 2. Verifies the connection with GET /engine/pages (expects 200)
|
|
10
|
+
* 3. Stores the key in the financial vault
|
|
11
|
+
* 4. Stores the webhook signing secret (optional)
|
|
12
|
+
*
|
|
13
|
+
* PRD Reference: PRD-kongo-integration.md §4.4 (adjusted from OAuth to manual)
|
|
14
|
+
*/
|
|
15
|
+
import { KongoClient, KongoApiError } from './client.js';
|
|
16
|
+
// ── Vault Keys ───────────────────────────────────────────
|
|
17
|
+
/** Vault key for the Kongo API key */
|
|
18
|
+
export const KONGO_API_KEY_VAULT_KEY = 'kongo-api-key';
|
|
19
|
+
/** Vault key for the webhook signing secret */
|
|
20
|
+
export const KONGO_WEBHOOK_SECRET_VAULT_KEY = 'kongo-webhook-secret';
|
|
21
|
+
/**
|
|
22
|
+
* Verify a Kongo API key by making a read-only API call.
|
|
23
|
+
* Returns connection status with page count on success.
|
|
24
|
+
*/
|
|
25
|
+
export async function verifyKongoConnection(apiKey) {
|
|
26
|
+
try {
|
|
27
|
+
const client = new KongoClient({ apiKey });
|
|
28
|
+
const result = await client.get('/engine/pages', { limit: 1 });
|
|
29
|
+
return {
|
|
30
|
+
connected: true,
|
|
31
|
+
pageCount: result.items.length > 0 ? undefined : 0, // Can't know total from first page
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
if (err instanceof KongoApiError) {
|
|
36
|
+
return {
|
|
37
|
+
connected: false,
|
|
38
|
+
error: `${err.code}: ${err.message}`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
connected: false,
|
|
43
|
+
error: err instanceof Error ? err.message : 'Unknown error',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Provision Kongo access during /cultivation install.
|
|
49
|
+
*
|
|
50
|
+
* Steps:
|
|
51
|
+
* 1. Validate key format (ke_live_ prefix)
|
|
52
|
+
* 2. Verify connection with read-only API call
|
|
53
|
+
* 3. Store key in financial vault
|
|
54
|
+
* 4. Optionally store webhook signing secret
|
|
55
|
+
*
|
|
56
|
+
* @param config - API key and optional webhook secret
|
|
57
|
+
* @param vaultSet - Financial vault setter (injected for testability)
|
|
58
|
+
*/
|
|
59
|
+
export async function provisionKongo(config, vaultSet) {
|
|
60
|
+
// Validate key format
|
|
61
|
+
if (!config.apiKey.startsWith('ke_live_')) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
message: 'Invalid API key format. Kongo keys use the ke_live_ prefix. Get yours at kongo.io/dashboard/api',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Verify connection
|
|
68
|
+
const connection = await verifyKongoConnection(config.apiKey);
|
|
69
|
+
if (!connection.connected) {
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
message: `Kongo connection failed: ${connection.error}`,
|
|
73
|
+
connection,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// Store in vault
|
|
77
|
+
await vaultSet(KONGO_API_KEY_VAULT_KEY, config.apiKey);
|
|
78
|
+
if (config.webhookSecret) {
|
|
79
|
+
await vaultSet(KONGO_WEBHOOK_SECRET_VAULT_KEY, config.webhookSecret);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
success: true,
|
|
83
|
+
message: 'Kongo connected successfully.',
|
|
84
|
+
connection,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// ── Connection Check ─────────────────────────────────────
|
|
88
|
+
/**
|
|
89
|
+
* Check if Kongo is connected (API key exists in vault).
|
|
90
|
+
*/
|
|
91
|
+
export async function isKongoConnected(vaultGet) {
|
|
92
|
+
const apiKey = await vaultGet(KONGO_API_KEY_VAULT_KEY);
|
|
93
|
+
return apiKey !== null && apiKey.startsWith('ke_live_');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Create a KongoClient from vault credentials.
|
|
97
|
+
* Returns null if Kongo is not connected.
|
|
98
|
+
*/
|
|
99
|
+
export async function getKongoClient(vaultGet) {
|
|
100
|
+
const apiKey = await vaultGet(KONGO_API_KEY_VAULT_KEY);
|
|
101
|
+
if (!apiKey)
|
|
102
|
+
return null;
|
|
103
|
+
try {
|
|
104
|
+
return new KongoClient({ apiKey });
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Remove Kongo credentials from vault (disconnect).
|
|
112
|
+
*/
|
|
113
|
+
export async function disconnectKongo(vaultDelete) {
|
|
114
|
+
await vaultDelete(KONGO_API_KEY_VAULT_KEY);
|
|
115
|
+
await vaultDelete(KONGO_WEBHOOK_SECRET_VAULT_KEY);
|
|
116
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kongo Seed — Extract seed content from PRDs for landing page generation.
|
|
3
|
+
*
|
|
4
|
+
* Phase 3.5 of /grow: Before you distribute, you need somewhere to send them.
|
|
5
|
+
*
|
|
6
|
+
* Extracts headline, value props, CTA, brand colors, and social proof from
|
|
7
|
+
* PRD YAML frontmatter and prose content. Maps to PrdSeedContent which
|
|
8
|
+
* feeds into createPageFromPrd().
|
|
9
|
+
*
|
|
10
|
+
* PRD Reference: PRD-kongo-integration.md §4.5
|
|
11
|
+
*/
|
|
12
|
+
import type { PrdSeedContent } from './types.js';
|
|
13
|
+
export interface PrdContent {
|
|
14
|
+
/** YAML frontmatter parsed as object */
|
|
15
|
+
readonly frontmatter: Record<string, unknown>;
|
|
16
|
+
/** Full PRD text content (after frontmatter) */
|
|
17
|
+
readonly body: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Extract PrdSeedContent from a PRD document.
|
|
21
|
+
*
|
|
22
|
+
* Strategy:
|
|
23
|
+
* 1. Use frontmatter `name` for project name
|
|
24
|
+
* 2. Extract first ## heading or overview section for headline
|
|
25
|
+
* 3. Extract value props from feature descriptions or bullet lists
|
|
26
|
+
* 4. Use brand colors from frontmatter `style` or defaults
|
|
27
|
+
* 5. Extract CTA from conversion or signup references
|
|
28
|
+
*/
|
|
29
|
+
export declare function extractSeedFromPrd(prd: PrdContent): PrdSeedContent;
|
|
30
|
+
/**
|
|
31
|
+
* Extract seed content for a specific campaign.
|
|
32
|
+
* Enriches the base PRD seed with campaign-specific metadata.
|
|
33
|
+
*
|
|
34
|
+
* Self-marketing mode: When the product domain matches the Kongo domain
|
|
35
|
+
* (dogfooding), destination URLs use the /lp/ direct-render path instead
|
|
36
|
+
* of subdomain URLs. This avoids the iframe sandbox constraint that breaks
|
|
37
|
+
* GA4 cookie tracking and UTM relay. See GROWTH_STRATEGIST.md "Iframe
|
|
38
|
+
* Sandbox Constraint" section.
|
|
39
|
+
*/
|
|
40
|
+
export declare function extractSeedForCampaign(prd: PrdContent, campaignId: string, platform: string, options?: {
|
|
41
|
+
kongoDomain?: string;
|
|
42
|
+
selfMarketing?: boolean;
|
|
43
|
+
slug?: string;
|
|
44
|
+
}): PrdSeedContent;
|
|
45
|
+
/**
|
|
46
|
+
* Parse PRD text into frontmatter and body.
|
|
47
|
+
* Handles ```yaml fenced frontmatter and --- delimited frontmatter.
|
|
48
|
+
*/
|
|
49
|
+
export declare function parsePrdContent(raw: string): PrdContent;
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kongo Seed — Extract seed content from PRDs for landing page generation.
|
|
3
|
+
*
|
|
4
|
+
* Phase 3.5 of /grow: Before you distribute, you need somewhere to send them.
|
|
5
|
+
*
|
|
6
|
+
* Extracts headline, value props, CTA, brand colors, and social proof from
|
|
7
|
+
* PRD YAML frontmatter and prose content. Maps to PrdSeedContent which
|
|
8
|
+
* feeds into createPageFromPrd().
|
|
9
|
+
*
|
|
10
|
+
* PRD Reference: PRD-kongo-integration.md §4.5
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Extract PrdSeedContent from a PRD document.
|
|
14
|
+
*
|
|
15
|
+
* Strategy:
|
|
16
|
+
* 1. Use frontmatter `name` for project name
|
|
17
|
+
* 2. Extract first ## heading or overview section for headline
|
|
18
|
+
* 3. Extract value props from feature descriptions or bullet lists
|
|
19
|
+
* 4. Use brand colors from frontmatter `style` or defaults
|
|
20
|
+
* 5. Extract CTA from conversion or signup references
|
|
21
|
+
*/
|
|
22
|
+
export function extractSeedFromPrd(prd) {
|
|
23
|
+
const fm = prd.frontmatter;
|
|
24
|
+
const body = prd.body;
|
|
25
|
+
const projectName = extractProjectName(fm);
|
|
26
|
+
const headline = extractHeadline(fm, body);
|
|
27
|
+
const subheadline = extractSubheadline(fm, body);
|
|
28
|
+
const valueProps = extractValueProps(body);
|
|
29
|
+
const { ctaText, ctaUrl } = extractCta(fm, body);
|
|
30
|
+
const brandColors = extractBrandColors(fm);
|
|
31
|
+
const socialProof = extractSocialProof(body);
|
|
32
|
+
return {
|
|
33
|
+
projectName,
|
|
34
|
+
headline,
|
|
35
|
+
subheadline,
|
|
36
|
+
valueProps,
|
|
37
|
+
ctaText,
|
|
38
|
+
ctaUrl,
|
|
39
|
+
brandColors,
|
|
40
|
+
socialProof: socialProof.length > 0 ? socialProof : undefined,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Extract seed content for a specific campaign.
|
|
45
|
+
* Enriches the base PRD seed with campaign-specific metadata.
|
|
46
|
+
*
|
|
47
|
+
* Self-marketing mode: When the product domain matches the Kongo domain
|
|
48
|
+
* (dogfooding), destination URLs use the /lp/ direct-render path instead
|
|
49
|
+
* of subdomain URLs. This avoids the iframe sandbox constraint that breaks
|
|
50
|
+
* GA4 cookie tracking and UTM relay. See GROWTH_STRATEGIST.md "Iframe
|
|
51
|
+
* Sandbox Constraint" section.
|
|
52
|
+
*/
|
|
53
|
+
export function extractSeedForCampaign(prd, campaignId, platform, options) {
|
|
54
|
+
const baseSeed = extractSeedFromPrd(prd);
|
|
55
|
+
const kongoDomain = options?.kongoDomain ?? 'kongo.io';
|
|
56
|
+
const slug = options?.slug ?? slugify(baseSeed.projectName, campaignId);
|
|
57
|
+
const isSelfMarketing = options?.selfMarketing ?? false;
|
|
58
|
+
// Self-marketing: use /lp/ direct-render path (no iframe sandbox)
|
|
59
|
+
// Standard: use subdomain URL
|
|
60
|
+
const destinationUrl = isSelfMarketing
|
|
61
|
+
? `https://${kongoDomain}/lp/${slug}`
|
|
62
|
+
: `https://${slug}.${kongoDomain}`;
|
|
63
|
+
return {
|
|
64
|
+
...baseSeed,
|
|
65
|
+
campaignId,
|
|
66
|
+
platform,
|
|
67
|
+
ctaUrl: destinationUrl,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Generate a URL-safe slug from project name and campaign ID */
|
|
71
|
+
function slugify(projectName, campaignId) {
|
|
72
|
+
return `${projectName}-${campaignId}`
|
|
73
|
+
.toLowerCase()
|
|
74
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
75
|
+
.replace(/-+/g, '-')
|
|
76
|
+
.replace(/^-|-$/g, '');
|
|
77
|
+
}
|
|
78
|
+
// ── Frontmatter Parsing ──────────────────────────────────
|
|
79
|
+
/**
|
|
80
|
+
* Parse PRD text into frontmatter and body.
|
|
81
|
+
* Handles ```yaml fenced frontmatter and --- delimited frontmatter.
|
|
82
|
+
*/
|
|
83
|
+
export function parsePrdContent(raw) {
|
|
84
|
+
const lines = raw.split('\n');
|
|
85
|
+
let frontmatterStr = '';
|
|
86
|
+
let bodyStart = 0;
|
|
87
|
+
// Try ```yaml fenced block
|
|
88
|
+
if (lines[0]?.trim() === '```yaml' || lines[0]?.trim() === '---') {
|
|
89
|
+
const delimiter = lines[0].trim() === '```yaml' ? '```' : '---';
|
|
90
|
+
let found = false;
|
|
91
|
+
for (let i = 1; i < lines.length; i++) {
|
|
92
|
+
if (lines[i].trim() === delimiter) {
|
|
93
|
+
bodyStart = i + 1;
|
|
94
|
+
found = true;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
frontmatterStr += lines[i] + '\n';
|
|
98
|
+
}
|
|
99
|
+
// No closing delimiter — treat as no frontmatter
|
|
100
|
+
if (!found) {
|
|
101
|
+
frontmatterStr = '';
|
|
102
|
+
bodyStart = 0;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Simple YAML key: value parsing (no dependency on yaml parser)
|
|
106
|
+
const frontmatter = {};
|
|
107
|
+
for (const line of frontmatterStr.split('\n')) {
|
|
108
|
+
const match = line.match(/^(\w[\w-]*)\s*:\s*(.+)/);
|
|
109
|
+
if (match) {
|
|
110
|
+
const key = match[1];
|
|
111
|
+
let value = match[2].trim();
|
|
112
|
+
// Strip quotes
|
|
113
|
+
if (typeof value === 'string' && value.startsWith('"') && value.endsWith('"')) {
|
|
114
|
+
value = value.slice(1, -1);
|
|
115
|
+
}
|
|
116
|
+
frontmatter[key] = value;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
frontmatter,
|
|
121
|
+
body: lines.slice(bodyStart).join('\n'),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// ── Extraction Helpers ───────────────────────────────────
|
|
125
|
+
function extractProjectName(fm) {
|
|
126
|
+
return String(fm.name ?? fm.projectName ?? fm.title ?? 'Untitled Project');
|
|
127
|
+
}
|
|
128
|
+
function extractHeadline(fm, body) {
|
|
129
|
+
// Check frontmatter tagline
|
|
130
|
+
if (fm.tagline)
|
|
131
|
+
return String(fm.tagline);
|
|
132
|
+
// First ## Overview section, or first sentence of body
|
|
133
|
+
const overviewMatch = body.match(/##\s+(?:Overview|Introduction|About)\s*\n+(.+)/i);
|
|
134
|
+
if (overviewMatch) {
|
|
135
|
+
return truncate(overviewMatch[1].trim(), 100);
|
|
136
|
+
}
|
|
137
|
+
// First meaningful paragraph
|
|
138
|
+
const firstParagraph = body.split('\n\n').find(p => p.trim().length > 20 && !p.startsWith('#'));
|
|
139
|
+
if (firstParagraph) {
|
|
140
|
+
return truncate(firstParagraph.split('.')[0].trim(), 100);
|
|
141
|
+
}
|
|
142
|
+
return String(fm.name ?? 'Your Next Big Thing');
|
|
143
|
+
}
|
|
144
|
+
function extractSubheadline(fm, body) {
|
|
145
|
+
if (fm.description)
|
|
146
|
+
return truncate(String(fm.description), 200);
|
|
147
|
+
// Second sentence of overview
|
|
148
|
+
const overviewMatch = body.match(/##\s+(?:Overview|Introduction|About)\s*\n+([\s\S]*?)(?=\n##|\n---)/i);
|
|
149
|
+
if (overviewMatch) {
|
|
150
|
+
const sentences = overviewMatch[1].trim().split(/\.\s+/);
|
|
151
|
+
if (sentences.length > 1)
|
|
152
|
+
return truncate(sentences[1].trim(), 200);
|
|
153
|
+
}
|
|
154
|
+
return `Built with ${String(fm.name ?? 'innovation')}`;
|
|
155
|
+
}
|
|
156
|
+
function extractValueProps(body) {
|
|
157
|
+
const props = [];
|
|
158
|
+
// Look for bullet lists in Features section
|
|
159
|
+
const featuresMatch = body.match(/##\s+(?:Features|Core Features|Key Features|Capabilities)\s*\n([\s\S]*?)(?=\n##|$)/i);
|
|
160
|
+
if (featuresMatch) {
|
|
161
|
+
const bullets = featuresMatch[1].match(/^[-*]\s+(.+)/gm);
|
|
162
|
+
if (bullets) {
|
|
163
|
+
for (const bullet of bullets.slice(0, 5)) {
|
|
164
|
+
props.push(bullet.replace(/^[-*]\s+/, '').trim());
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Fallback: any bullet lists in the first half of the document
|
|
169
|
+
if (props.length === 0) {
|
|
170
|
+
const halfBody = body.slice(0, body.length / 2);
|
|
171
|
+
const bullets = halfBody.match(/^[-*]\s+(.+)/gm);
|
|
172
|
+
if (bullets) {
|
|
173
|
+
for (const bullet of bullets.slice(0, 5)) {
|
|
174
|
+
props.push(bullet.replace(/^[-*]\s+/, '').trim());
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Ensure at least 3 value props
|
|
179
|
+
while (props.length < 3) {
|
|
180
|
+
props.push(props.length === 0 ? 'Built for speed' : props.length === 1 ? 'Easy to use' : 'Trusted by teams');
|
|
181
|
+
}
|
|
182
|
+
return props.slice(0, 5);
|
|
183
|
+
}
|
|
184
|
+
function extractCta(fm, body) {
|
|
185
|
+
// Check frontmatter for CTA
|
|
186
|
+
if (fm.ctaText && fm.ctaUrl) {
|
|
187
|
+
return { ctaText: String(fm.ctaText), ctaUrl: String(fm.ctaUrl) };
|
|
188
|
+
}
|
|
189
|
+
// Look for signup/demo/trial references
|
|
190
|
+
const ctaPatterns = [
|
|
191
|
+
{ pattern: /sign\s*up|get\s*started|start\s*(free\s*)?trial/i, text: 'Get Started Free' },
|
|
192
|
+
{ pattern: /request\s*(a\s*)?demo/i, text: 'Request a Demo' },
|
|
193
|
+
{ pattern: /join\s*waitlist/i, text: 'Join the Waitlist' },
|
|
194
|
+
{ pattern: /download/i, text: 'Download Now' },
|
|
195
|
+
{ pattern: /subscribe/i, text: 'Subscribe' },
|
|
196
|
+
];
|
|
197
|
+
for (const { pattern, text } of ctaPatterns) {
|
|
198
|
+
if (pattern.test(body))
|
|
199
|
+
return { ctaText: text, ctaUrl: '#signup' };
|
|
200
|
+
}
|
|
201
|
+
return { ctaText: 'Get Started', ctaUrl: '#signup' };
|
|
202
|
+
}
|
|
203
|
+
function extractBrandColors(fm) {
|
|
204
|
+
const style = fm.style;
|
|
205
|
+
const colors = style?.colors;
|
|
206
|
+
return {
|
|
207
|
+
primary: colors?.primary ?? '#1a1a2e',
|
|
208
|
+
secondary: colors?.secondary ?? '#16213e',
|
|
209
|
+
accent: colors?.accent ?? '#0f3460',
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function extractSocialProof(body) {
|
|
213
|
+
const proof = [];
|
|
214
|
+
// Look for testimonials section
|
|
215
|
+
const testimonialsMatch = body.match(/##\s+(?:Testimonials|Social Proof|What .+ Say)\s*\n([\s\S]*?)(?=\n##|$)/i);
|
|
216
|
+
if (testimonialsMatch) {
|
|
217
|
+
const quotes = testimonialsMatch[1].match(/>\s*(.+)/gm);
|
|
218
|
+
if (quotes) {
|
|
219
|
+
for (const quote of quotes.slice(0, 3)) {
|
|
220
|
+
proof.push(quote.replace(/^>\s*/, '').trim());
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Look for stats
|
|
225
|
+
const statsMatch = body.match(/(\d+[+kKmM]?\s+(?:users|teams|companies|customers|downloads|stars))/gi);
|
|
226
|
+
if (statsMatch) {
|
|
227
|
+
for (const stat of statsMatch.slice(0, 2)) {
|
|
228
|
+
proof.push(stat.trim());
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return proof;
|
|
232
|
+
}
|
|
233
|
+
function truncate(str, maxLen) {
|
|
234
|
+
if (str.length <= maxLen)
|
|
235
|
+
return str;
|
|
236
|
+
return str.slice(0, maxLen - 3) + '...';
|
|
237
|
+
}
|