git-paid 2.1.2 → 2.1.4
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/README.md +22 -34
- package/bin/git-paid.js +127 -52
- package/lib/auth.js +866 -208
- package/lib/directories.js +123 -123
- package/lib/engine.js +228 -112
- package/lib/fixer.js +58 -0
- package/lib/inbox.js +359 -72
- package/lib/mcp.js +220 -87
- package/lib/pseo.js +290 -106
- package/lib/safe-fetch.js +239 -0
- package/lib/stateful-mcp.js +16 -4
- package/lib/submitter.js +89 -28
- package/package.json +53 -44
- package/brand/README.md +0 -54
- package/brand/postbuild-icon.png +0 -0
- package/brand/postbuild-icon.svg +0 -14
- package/brand/postbuild-logo.png +0 -0
- package/brand/postbuild-logo.svg +0 -17
- package/lib/stripe.js +0 -554
- package/server.js +0 -252
package/lib/mcp.js
CHANGED
|
@@ -7,9 +7,22 @@ import fs from "node:fs";
|
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import readline from "node:readline";
|
|
9
9
|
import { scanTargetUrl, searchBuyerIntentRadar, dispatchDirectories, DIRECTORIES } from "./engine.js";
|
|
10
|
+
import { verifyCliLicense } from "./auth.js";
|
|
10
11
|
import { applyLocalSeoFixes } from "./fixer.js";
|
|
11
|
-
import { listInboxMessages, sendTestVerificationEmail } from "./inbox.js";
|
|
12
|
-
import { withStatefulMcp, createGenericEntityAdapter } from "./stateful-mcp.js";
|
|
12
|
+
import { listInboxMessages, sendTestVerificationEmail, getBotInboxForDomain, markMessageVerified } from "./inbox.js";
|
|
13
|
+
import { withStatefulMcp, createGenericEntityAdapter, spotlightUntrustedText } from "./stateful-mcp.js";
|
|
14
|
+
import { safeFetch } from "./safe-fetch.js";
|
|
15
|
+
|
|
16
|
+
const ALLOWED_VERIFY_DOMAINS = [
|
|
17
|
+
"postbuild.dev",
|
|
18
|
+
...DIRECTORIES.map((d) => {
|
|
19
|
+
try {
|
|
20
|
+
return new URL(d.url).hostname.replace(/^www\./i, "").toLowerCase();
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}).filter(Boolean)
|
|
25
|
+
];
|
|
13
26
|
|
|
14
27
|
// ─── 1. STATEFUL MCP ENTITY ADAPTERS ──────────────────────────────────────────
|
|
15
28
|
|
|
@@ -42,7 +55,7 @@ export const inboxEntityAdapter = createGenericEntityAdapter({
|
|
|
42
55
|
updatedAtField: "receivedAt",
|
|
43
56
|
stubFields: ["id", "from", "otpCode", "primaryLink", "autoVerified"],
|
|
44
57
|
journalFields: ["verificationLinks"],
|
|
45
|
-
untrustedTextFields: ["subject", "snippet"],
|
|
58
|
+
untrustedTextFields: ["from", "subject", "snippet", "primaryLink", "otpCode"],
|
|
46
59
|
isActiveRecord: () => true
|
|
47
60
|
});
|
|
48
61
|
|
|
@@ -83,7 +96,7 @@ export const mcpRadar = withStatefulMcp({
|
|
|
83
96
|
|
|
84
97
|
const domainDirectoryState = new Map();
|
|
85
98
|
|
|
86
|
-
function getDomainDirectories(targetUrl = "https://postbuild.dev", report = null) {
|
|
99
|
+
function getDomainDirectories(targetUrl = "https://www.postbuild.dev", report = null) {
|
|
87
100
|
const domain = targetUrl.replace(/^https?:\/\//i, "").replace(/^www\./i, "").split("/")[0] || "postbuild.dev";
|
|
88
101
|
if (!domainDirectoryState.has(domain)) {
|
|
89
102
|
const baseDirs = (report?.directories || DIRECTORIES).map((d, idx) => ({
|
|
@@ -110,46 +123,56 @@ function getDomainDirectories(targetUrl = "https://postbuild.dev", report = null
|
|
|
110
123
|
return domainDirectoryState.get(domain);
|
|
111
124
|
}
|
|
112
125
|
|
|
113
|
-
// ─── 2. HANDS-FREE INBOX AUTO-VERIFIER
|
|
126
|
+
// ─── 2. HANDS-FREE INBOX AUTO-VERIFIER (OPT-IN & SSRF-ALLOWLISTED) ────────────
|
|
114
127
|
|
|
115
|
-
export async function autoVerifyInboxLinks(inboxAddress = "
|
|
116
|
-
const
|
|
128
|
+
export async function autoVerifyInboxLinks(inboxAddress = "", options = {}) {
|
|
129
|
+
const shouldAutoVerify = options.autoVerify === true;
|
|
130
|
+
const targetMessageId = options.messageId ? String(options.messageId).trim() : "";
|
|
131
|
+
const res = await listInboxMessages(inboxAddress || getBotInboxForDomain("postbuild.dev"), {
|
|
132
|
+
allowRemoteFallback: options.allowRemoteFallback,
|
|
133
|
+
bearerToken: options.bearerToken
|
|
134
|
+
});
|
|
117
135
|
const verificationResults = [];
|
|
118
136
|
|
|
119
137
|
for (const m of res.messages) {
|
|
138
|
+
const shouldVerifyThisRow =
|
|
139
|
+
shouldAutoVerify && (!targetMessageId || m.id === targetMessageId);
|
|
120
140
|
const entry = {
|
|
121
141
|
id: m.id,
|
|
122
142
|
from: m.from,
|
|
123
143
|
subject: m.subject,
|
|
124
144
|
otpCode: m.otpCode || null,
|
|
125
|
-
primaryLink: m.primaryLink
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
primaryLink: m.primaryLink ? spotlightUntrustedText(m.primaryLink) : null,
|
|
146
|
+
verificationUrl: m.verificationUrl || (m.primaryLink && /^https:\/\//i.test(m.primaryLink) ? m.primaryLink : null),
|
|
147
|
+
autoVerified: Boolean(m.autoVerified || m.status === "verified"),
|
|
148
|
+
status: m.status,
|
|
149
|
+
httpStatus: m.httpStatus ?? null,
|
|
128
150
|
version_etag: m.autoVerified ? "v2" : "v1",
|
|
129
151
|
receivedAt: m.receivedAt,
|
|
130
152
|
snippet: m.snippet
|
|
131
153
|
};
|
|
132
154
|
|
|
133
|
-
if (m.primaryLink && /^https?:\/\//i.test(m.primaryLink)) {
|
|
155
|
+
if (shouldVerifyThisRow && !entry.autoVerified && m.primaryLink && /^https?:\/\//i.test(m.primaryLink)) {
|
|
134
156
|
try {
|
|
135
|
-
const
|
|
136
|
-
const timer = setTimeout(() => controller.abort(), 4500);
|
|
137
|
-
const resp = await fetch(m.primaryLink, {
|
|
157
|
+
const resp = await safeFetch(m.primaryLink, {
|
|
138
158
|
method: "GET",
|
|
139
|
-
|
|
140
|
-
|
|
159
|
+
timeoutMs: 4500,
|
|
160
|
+
allowedDomains: ALLOWED_VERIFY_DOMAINS,
|
|
161
|
+
lookupFn: options.lookupFn,
|
|
141
162
|
headers: {
|
|
142
|
-
"User-Agent": "Postbuild-
|
|
163
|
+
"User-Agent": "Postbuild-Verifier/2.1 (+https://www.postbuild.dev)"
|
|
143
164
|
}
|
|
144
165
|
});
|
|
145
|
-
clearTimeout(timer);
|
|
146
166
|
entry.autoVerified = resp.ok || resp.status < 400;
|
|
147
167
|
entry.httpStatus = resp.status;
|
|
168
|
+
entry.status = entry.autoVerified ? "verified" : m.status;
|
|
148
169
|
entry.version_etag = "v2";
|
|
170
|
+
if (entry.autoVerified) {
|
|
171
|
+
markMessageVerified(m.id, true, resp.status);
|
|
172
|
+
}
|
|
149
173
|
} catch {
|
|
150
|
-
entry.autoVerified =
|
|
151
|
-
entry.httpStatus =
|
|
152
|
-
entry.version_etag = "v2";
|
|
174
|
+
entry.autoVerified = false;
|
|
175
|
+
entry.httpStatus = 0;
|
|
153
176
|
}
|
|
154
177
|
}
|
|
155
178
|
|
|
@@ -160,7 +183,9 @@ export async function autoVerifyInboxLinks(inboxAddress = "postbuild-bot@postbui
|
|
|
160
183
|
inboxAddress: res.inboxAddress,
|
|
161
184
|
totalMessages: verificationResults.length,
|
|
162
185
|
verifiedCount: verificationResults.filter((x) => x.autoVerified).length,
|
|
163
|
-
extractedOtps: verificationResults
|
|
186
|
+
extractedOtps: verificationResults
|
|
187
|
+
.filter((x) => x.otpCode)
|
|
188
|
+
.map((x) => ({ from: spotlightUntrustedText(x.from), otp: spotlightUntrustedText(x.otpCode) })),
|
|
164
189
|
messages: verificationResults
|
|
165
190
|
};
|
|
166
191
|
}
|
|
@@ -242,21 +267,20 @@ export async function runStatefulMcpSimulation({
|
|
|
242
267
|
// ─── 4. OBSIDIAN VAULTOS TELEMETRY & DASHBOARD SYNC ───────────────────────────
|
|
243
268
|
|
|
244
269
|
export async function syncPostbuildToVault({
|
|
245
|
-
vaultPath =
|
|
246
|
-
urls = ["https://postbuild.dev"
|
|
247
|
-
sessionId = "vaultos-sync"
|
|
270
|
+
vaultPath = process.cwd(),
|
|
271
|
+
urls = ["https://www.postbuild.dev"],
|
|
272
|
+
sessionId = "vaultos-sync",
|
|
273
|
+
bearerToken = ""
|
|
248
274
|
} = {}) {
|
|
249
275
|
const projectsData = [];
|
|
250
276
|
|
|
251
277
|
for (const targetUrl of urls) {
|
|
252
278
|
const report = await scanTargetUrl(targetUrl);
|
|
253
279
|
const state = getDomainDirectories(targetUrl, report);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
const verifiedInbox = await autoVerifyInboxLinks(report.botEmail);
|
|
280
|
+
const verifiedInbox = await autoVerifyInboxLinks(report.botEmail, {
|
|
281
|
+
autoVerify: false,
|
|
282
|
+
bearerToken
|
|
283
|
+
});
|
|
260
284
|
|
|
261
285
|
const dirsQuery = await mcpDirectories.queryWithSupersetCache({
|
|
262
286
|
sessionId,
|
|
@@ -281,7 +305,7 @@ export async function syncPostbuildToVault({
|
|
|
281
305
|
botEmail: report.botEmail,
|
|
282
306
|
inboxCount: verifiedInbox.totalMessages,
|
|
283
307
|
autoVerifiedLinks: verifiedInbox.verifiedCount,
|
|
284
|
-
latestOtp: verifiedInbox.extractedOtps[0]?.otp || "
|
|
308
|
+
latestOtp: verifiedInbox.extractedOtps[0]?.otp || "None",
|
|
285
309
|
mcpReductionPct: mcpPass2._mcp?.payload_bytes?.reduction_pct || "-75.4%",
|
|
286
310
|
mcpQueryMs: mcpPass2._mcp?.query_ms || 0.04,
|
|
287
311
|
updatedAt: new Date().toISOString()
|
|
@@ -311,26 +335,54 @@ export async function syncPostbuildToVault({
|
|
|
311
335
|
};
|
|
312
336
|
|
|
313
337
|
const writtenVaultFiles = [];
|
|
338
|
+
const effectiveTarget = fs.existsSync(vaultPath) ? vaultPath : process.cwd();
|
|
339
|
+
const isObsidianVault =
|
|
340
|
+
fs.existsSync(path.join(effectiveTarget, ".obsidian")) ||
|
|
341
|
+
fs.existsSync(path.join(effectiveTarget, "_dashboards"));
|
|
314
342
|
|
|
315
|
-
if (
|
|
316
|
-
const metaDir = path.join(
|
|
343
|
+
if (isObsidianVault) {
|
|
344
|
+
const metaDir = path.join(effectiveTarget, "_meta");
|
|
317
345
|
fs.mkdirSync(metaDir, { recursive: true });
|
|
318
346
|
const metaFile = path.join(metaDir, "postbuild-telemetry.json");
|
|
319
347
|
fs.writeFileSync(metaFile, JSON.stringify(telemetryPayload, null, 2), "utf8");
|
|
320
348
|
writtenVaultFiles.push(metaFile);
|
|
321
349
|
|
|
322
|
-
const dashboardsDir = path.join(
|
|
350
|
+
const dashboardsDir = path.join(effectiveTarget, "_dashboards");
|
|
323
351
|
if (fs.existsSync(dashboardsDir)) {
|
|
324
352
|
const pbDashboardPath = path.join(dashboardsDir, "Postbuild Launch.md");
|
|
325
353
|
const pbDashboardContent = buildVaultOsPostbuildDashboardMarkdown();
|
|
326
354
|
fs.writeFileSync(pbDashboardPath, pbDashboardContent, "utf8");
|
|
327
355
|
writtenVaultFiles.push(pbDashboardPath);
|
|
328
356
|
}
|
|
357
|
+
} else {
|
|
358
|
+
const pbDir = path.join(effectiveTarget, ".postbuild");
|
|
359
|
+
fs.mkdirSync(pbDir, { recursive: true });
|
|
360
|
+
const jsonFile = path.join(pbDir, "telemetry.json");
|
|
361
|
+
fs.writeFileSync(jsonFile, JSON.stringify(telemetryPayload, null, 2), "utf8");
|
|
362
|
+
writtenVaultFiles.push(jsonFile);
|
|
363
|
+
|
|
364
|
+
const mdLines = [
|
|
365
|
+
"# postbuild Launch & SEO Status Report",
|
|
366
|
+
"",
|
|
367
|
+
`> Synced: \`${telemetryPayload.generatedAt}\` via \`postbuild-mcp\``,
|
|
368
|
+
"",
|
|
369
|
+
"| Domain | Readiness Score | Checks | Programmatic SEO | Directories | Bot Inbox | Latest OTP |",
|
|
370
|
+
"| :--- | :--- | :--- | :--- | :--- | :--- | :--- |",
|
|
371
|
+
...telemetryPayload.projects.map(
|
|
372
|
+
(p) =>
|
|
373
|
+
`| **${p.domain}** | **${p.readinessScore}/100** | ${p.passedChecks}/${p.totalChecks} | ${p.pseoCount} Routes | ${p.directoryCount} (${p.autoVerifiedLinks} Verified) | \`${p.botEmail}\` | \`${p.latestOtp}\` |`
|
|
374
|
+
),
|
|
375
|
+
""
|
|
376
|
+
];
|
|
377
|
+
const mdFile = path.join(effectiveTarget, "LAUNCH_STATUS.md");
|
|
378
|
+
fs.writeFileSync(mdFile, mdLines.join("\n"), "utf8");
|
|
379
|
+
writtenVaultFiles.push(mdFile);
|
|
329
380
|
}
|
|
330
381
|
|
|
331
382
|
return {
|
|
332
383
|
ok: true,
|
|
333
|
-
|
|
384
|
+
mode: isObsidianVault ? "obsidian_vault" : "markdown_repo",
|
|
385
|
+
vaultPath: effectiveTarget,
|
|
334
386
|
writtenVaultFiles,
|
|
335
387
|
telemetry: telemetryPayload
|
|
336
388
|
};
|
|
@@ -339,7 +391,7 @@ export async function syncPostbuildToVault({
|
|
|
339
391
|
function buildVaultOsPostbuildDashboardMarkdown() {
|
|
340
392
|
return `---
|
|
341
393
|
aliases:
|
|
342
|
-
- Postbuild Launch
|
|
394
|
+
- Postbuild Launch Dashboard
|
|
343
395
|
- "_dashboards/Postbuild Launch"
|
|
344
396
|
tags:
|
|
345
397
|
- dashboard
|
|
@@ -351,7 +403,7 @@ cssclasses:
|
|
|
351
403
|
---
|
|
352
404
|
|
|
353
405
|
\`\`\`dataviewjs
|
|
354
|
-
// ─── POSTBUILD MCP LAUNCH
|
|
406
|
+
// ─── POSTBUILD MCP LAUNCH DASHBOARD ──────────────────────────────────────────
|
|
355
407
|
// Reads cached telemetry from _meta/postbuild-telemetry.json (<2ms render)
|
|
356
408
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
357
409
|
|
|
@@ -391,8 +443,8 @@ root.innerHTML = \`
|
|
|
391
443
|
|
|
392
444
|
<div class="hero-strip">
|
|
393
445
|
<div>
|
|
394
|
-
<div class="hero-title">Postbuild Launch & SEO
|
|
395
|
-
<div class="hero-subtitle">Managed autonomously by
|
|
446
|
+
<div class="hero-title">Postbuild Launch & SEO Dashboard</div>
|
|
447
|
+
<div class="hero-subtitle">Managed autonomously by Postbuild MCP (<code>npx git-paid mcp</code>)</div>
|
|
396
448
|
</div>
|
|
397
449
|
<div class="stat-group">
|
|
398
450
|
<div class="stat-block">
|
|
@@ -422,7 +474,7 @@ root.innerHTML = \`
|
|
|
422
474
|
<span class="badge" style="background:rgba(16,185,129,0.15); color:#10b981; border:1px solid rgba(16,185,129,0.3);">LIVE AUDITS</span>
|
|
423
475
|
Domain Readiness, pSEO & Directory Dispatch
|
|
424
476
|
</div>
|
|
425
|
-
<a class="action-btn" href="https://postbuild.dev" style="text-decoration:none;">Open Studio -></a>
|
|
477
|
+
<a class="action-btn" href="https://www.postbuild.dev" style="text-decoration:none;">Open Studio -></a>
|
|
426
478
|
</div>
|
|
427
479
|
<div style="display:flex; flex-direction:column; gap:8px;">
|
|
428
480
|
\${projects.map(p => \`
|
|
@@ -431,7 +483,7 @@ root.innerHTML = \`
|
|
|
431
483
|
<strong style="font-size:14px;">\${p.domain}</strong>
|
|
432
484
|
<span class="badge badge-cyan" style="margin-left:8px;">\${p.passedChecks}/\${p.totalChecks} Checks Passed</span>
|
|
433
485
|
<div style="font-size:11.5px; color:var(--text-muted); margin-top:4px;">
|
|
434
|
-
<code>\${p.botEmail}</code> · \${p.pseoCount} pSEO Routes · \${p.directoryCount} Directories (\${p.autoVerifiedLinks}
|
|
486
|
+
<code>\${p.botEmail}</code> · \${p.pseoCount} pSEO Routes · \${p.directoryCount} Directories (\${p.autoVerifiedLinks} Verified) · Latest OTP: <code>\${p.latestOtp}</code>
|
|
435
487
|
</div>
|
|
436
488
|
</div>
|
|
437
489
|
<div style="text-align:right;">
|
|
@@ -469,7 +521,7 @@ root.innerHTML = \`
|
|
|
469
521
|
`;
|
|
470
522
|
}
|
|
471
523
|
|
|
472
|
-
// ─── 5. AGENT SKILL & CONFIG SCAFFOLDER (`npx
|
|
524
|
+
// ─── 5. AGENT SKILL & CONFIG SCAFFOLDER (`npx git-paid skill`) ───────────────
|
|
473
525
|
|
|
474
526
|
export function installAgentSkillFiles({ cwd = process.cwd() } = {}) {
|
|
475
527
|
const skillDir = path.join(cwd, ".agent", "skills", "postbuild");
|
|
@@ -478,12 +530,12 @@ export function installAgentSkillFiles({ cwd = process.cwd() } = {}) {
|
|
|
478
530
|
const skillMdPath = path.join(skillDir, "SKILL.md");
|
|
479
531
|
const skillMd = `---
|
|
480
532
|
name: postbuild
|
|
481
|
-
description: Autonomous Post-Build SEO, 85-Directory
|
|
533
|
+
description: Autonomous Post-Build SEO, 85-Directory Launch Kit, @postbuild.dev Inbox Verifier, and Markdown Launch Status Sync powered by Postbuild MCP (git-paid).
|
|
482
534
|
---
|
|
483
535
|
|
|
484
|
-
# Postbuild (\`git
|
|
536
|
+
# Postbuild (\`git-paid\`) Agent Skill
|
|
485
537
|
|
|
486
|
-
Use the \`
|
|
538
|
+
Use the \`git-paid\` MCP server (\`npx -y git-paid mcp\`) or CLI (\`npx git-paid\`) to audit, fix, monetize, and launch any web project.
|
|
487
539
|
|
|
488
540
|
## MCP Server Configuration (\`.mcp.json\` / \`mcp_config.json\`)
|
|
489
541
|
\`\`\`json
|
|
@@ -491,7 +543,7 @@ Use the \`postbuild\` MCP server (\`npx postbuild mcp\`) or CLI (\`npx postbuild
|
|
|
491
543
|
"mcpServers": {
|
|
492
544
|
"postbuild": {
|
|
493
545
|
"command": "npx",
|
|
494
|
-
"args": ["-y", "
|
|
546
|
+
"args": ["-y", "git-paid", "mcp"]
|
|
495
547
|
}
|
|
496
548
|
}
|
|
497
549
|
}
|
|
@@ -499,11 +551,11 @@ Use the \`postbuild\` MCP server (\`npx postbuild mcp\`) or CLI (\`npx postbuild
|
|
|
499
551
|
|
|
500
552
|
## Available Postbuild MCP Tools
|
|
501
553
|
1. \`postbuild_scan\` — Run the 8-check live Monetization & SEO audit on any URL.
|
|
502
|
-
2. \`postbuild_directories\` — Query the 85 startup & AI directories using In-Memory Superset Caching
|
|
554
|
+
2. \`postbuild_directories\` — Query the 85 startup & AI directories using In-Memory Superset Caching + Watermark Context Residuals (\`unchanged@vN\`).
|
|
503
555
|
3. \`postbuild_pseo\` — Query the 25 Programmatic SEO routes with stateful delta compaction.
|
|
504
|
-
4. \`postbuild_dispatch\` —
|
|
505
|
-
5. \`postbuild_inbox\` — Read \`<slug
|
|
506
|
-
6. \`postbuild_vault_sync\` —
|
|
556
|
+
4. \`postbuild_dispatch\` — Prepare 85 prefilled launch links and ping IndexNow/WebSub protected by Two-Phase Cryptographic OCC (\`dry_run: true\` issues \`commit_token: "ct_..."\`; \`dry_run: false\` commits).
|
|
557
|
+
5. \`postbuild_inbox\` — Read \`<slug>-<hash>@postbuild.dev\` with Untrusted Content Spotlighting and optionally verify allowlisted directory confirmation links (\`auto_verify: true\`).
|
|
558
|
+
6. \`postbuild_vault_sync\` — Write \`LAUNCH_STATUS.md\` and \`.postbuild/telemetry.json\` to any repository (or sync to an Obsidian VaultOS dashboard if present).
|
|
507
559
|
`;
|
|
508
560
|
fs.writeFileSync(skillMdPath, skillMd, "utf8");
|
|
509
561
|
|
|
@@ -514,24 +566,24 @@ Use the \`postbuild\` MCP server (\`npx postbuild mcp\`) or CLI (\`npx postbuild
|
|
|
514
566
|
mcpServers: {
|
|
515
567
|
postbuild: {
|
|
516
568
|
command: "npx",
|
|
517
|
-
args: ["-y", "
|
|
569
|
+
args: ["-y", "git-paid", "mcp"]
|
|
518
570
|
}
|
|
519
571
|
}
|
|
520
572
|
}
|
|
521
573
|
};
|
|
522
574
|
}
|
|
523
575
|
|
|
524
|
-
// ─── 6. STDIO JSON-RPC 2.0 MCP SERVER (`npx
|
|
576
|
+
// ─── 6. STDIO JSON-RPC 2.0 MCP SERVER (`npx git-paid mcp`) ───────────────────
|
|
525
577
|
|
|
526
578
|
export const MCP_TOOLS = [
|
|
527
579
|
{
|
|
528
580
|
name: "postbuild_scan",
|
|
529
581
|
description:
|
|
530
|
-
"Run an 8-point Post-Build Monetization, AI Crawler, & SEO audit on any URL and provision a dedicated <slug
|
|
582
|
+
"Run an 8-point Post-Build Monetization, AI Crawler, & SEO audit on any URL and provision a dedicated <slug>-<hash>@postbuild.dev verification inbox.",
|
|
531
583
|
inputSchema: {
|
|
532
584
|
type: "object",
|
|
533
585
|
properties: {
|
|
534
|
-
url: { type: "string", description: "Target URL or domain (e.g. https://postbuild.dev)" }
|
|
586
|
+
url: { type: "string", description: "Target URL or domain (e.g. https://www.postbuild.dev)" }
|
|
535
587
|
},
|
|
536
588
|
required: ["url"]
|
|
537
589
|
}
|
|
@@ -539,11 +591,11 @@ export const MCP_TOOLS = [
|
|
|
539
591
|
{
|
|
540
592
|
name: "postbuild_directories",
|
|
541
593
|
description:
|
|
542
|
-
"Search and filter the 85 startup, AI, and developer directories using In-Memory Superset Cache and Watermark Context Residuals (unchanged@vN / updated@vN)
|
|
594
|
+
"Search and filter the 85 startup, AI, and developer directories using In-Memory Superset Cache and Watermark Context Residuals (unchanged@vN / updated@vN).",
|
|
543
595
|
inputSchema: {
|
|
544
596
|
type: "object",
|
|
545
597
|
properties: {
|
|
546
|
-
url: { type: "string", description: "Target URL (default: https://postbuild.dev)" },
|
|
598
|
+
url: { type: "string", description: "Target URL (default: https://www.postbuild.dev)" },
|
|
547
599
|
category: { type: "string", description: "Optional category filter (e.g. AI, SaaS, Developer, Launch)" },
|
|
548
600
|
status: { type: "string", description: "Optional status filter (ready_auto, queued, dispatched)" },
|
|
549
601
|
min_da: { type: "number", description: "Minimum Domain Rating (0-100)" },
|
|
@@ -568,7 +620,7 @@ export const MCP_TOOLS = [
|
|
|
568
620
|
{
|
|
569
621
|
name: "postbuild_dispatch",
|
|
570
622
|
description:
|
|
571
|
-
"
|
|
623
|
+
"Prepare 85 prefilled directory links and ping IndexNow/WebSub with Two-Phase Cryptographic OCC Guard. Call with dry_run: true first to receive a commit_token (ct_...), then call with dry_run: false and commit_token to commit.",
|
|
572
624
|
inputSchema: {
|
|
573
625
|
type: "object",
|
|
574
626
|
properties: {
|
|
@@ -576,7 +628,7 @@ export const MCP_TOOLS = [
|
|
|
576
628
|
directory_ids: {
|
|
577
629
|
type: "array",
|
|
578
630
|
items: { type: "string" },
|
|
579
|
-
description: "Optional subset of directory IDs
|
|
631
|
+
description: "Optional subset of directory IDs"
|
|
580
632
|
},
|
|
581
633
|
expected_version: { type: "string", description: "Expected version watermark (e.g. v1)" },
|
|
582
634
|
dry_run: { type: "boolean", description: "Phase 1 preview mode (default: true)" },
|
|
@@ -588,13 +640,12 @@ export const MCP_TOOLS = [
|
|
|
588
640
|
{
|
|
589
641
|
name: "postbuild_inbox",
|
|
590
642
|
description:
|
|
591
|
-
"Check the dedicated <slug
|
|
643
|
+
"Check the dedicated <slug>-<hash>@postbuild.dev verification inbox with Untrusted Content Spotlighting. Set auto_verify: true to follow allowlisted directory confirmation links.",
|
|
592
644
|
inputSchema: {
|
|
593
645
|
type: "object",
|
|
594
646
|
properties: {
|
|
595
|
-
inbox: { type: "string", description: "Bot inbox address
|
|
596
|
-
auto_verify: { type: "boolean", description: "
|
|
597
|
-
send_test: { type: "boolean", description: "Send a test verification email first" }
|
|
647
|
+
inbox: { type: "string", description: "Bot inbox address" },
|
|
648
|
+
auto_verify: { type: "boolean", description: "Opt-in: follow allowlisted confirmation links (default: false)" }
|
|
598
649
|
}
|
|
599
650
|
}
|
|
600
651
|
},
|
|
@@ -614,13 +665,13 @@ export const MCP_TOOLS = [
|
|
|
614
665
|
{
|
|
615
666
|
name: "postbuild_vault_sync",
|
|
616
667
|
description:
|
|
617
|
-
"
|
|
668
|
+
"Write LAUNCH_STATUS.md and .postbuild/telemetry.json into a local project directory (or sync to an Obsidian VaultOS dashboard if .obsidian/ is present).",
|
|
618
669
|
inputSchema: {
|
|
619
670
|
type: "object",
|
|
620
671
|
properties: {
|
|
621
672
|
vault_path: {
|
|
622
673
|
type: "string",
|
|
623
|
-
description: "
|
|
674
|
+
description: "Target repository or Obsidian vault path (defaults to current working directory)"
|
|
624
675
|
},
|
|
625
676
|
urls: {
|
|
626
677
|
type: "array",
|
|
@@ -632,28 +683,40 @@ export const MCP_TOOLS = [
|
|
|
632
683
|
}
|
|
633
684
|
];
|
|
634
685
|
|
|
635
|
-
export async function executeMcpToolCall(name, args = {}, sessionId = "mcp-stdio-session") {
|
|
686
|
+
export async function executeMcpToolCall(name, args = {}, sessionId = "mcp-stdio-session", authToken = "") {
|
|
636
687
|
if (name === "postbuild_scan") {
|
|
637
|
-
const report = await scanTargetUrl(args.url || "https://postbuild.dev");
|
|
688
|
+
const report = await scanTargetUrl(args.url || "https://www.postbuild.dev");
|
|
689
|
+
const license = await verifyCliLicense({ domain: report.domain, token: authToken });
|
|
638
690
|
getDomainDirectories(report.targetUrl, report);
|
|
639
691
|
return {
|
|
640
692
|
domain: report.domain,
|
|
641
693
|
targetUrl: report.targetUrl,
|
|
642
694
|
readinessScore: report.readinessScore,
|
|
643
695
|
botEmail: report.botEmail,
|
|
696
|
+
licenseStatus: license.valid ? "PRO_ACTIVE" : "FREE_PREVIEW",
|
|
644
697
|
checks: report.checks,
|
|
645
698
|
pseoRouteCount: report.pseoPages.length,
|
|
646
|
-
directoryCount: report.directoryCount
|
|
699
|
+
directoryCount: report.directoryCount,
|
|
700
|
+
...(license.valid
|
|
701
|
+
? {}
|
|
702
|
+
: {
|
|
703
|
+
upgradeNotice: license.message,
|
|
704
|
+
checkoutUrl: license.checkoutUrl,
|
|
705
|
+
loginCommand: "npx git-paid login <your-pb_live_key>"
|
|
706
|
+
})
|
|
647
707
|
};
|
|
648
708
|
}
|
|
649
709
|
|
|
650
710
|
if (name === "postbuild_directories") {
|
|
651
|
-
const targetUrl = args.url || "https://postbuild.dev";
|
|
711
|
+
const targetUrl = args.url || "https://www.postbuild.dev";
|
|
652
712
|
const state = getDomainDirectories(targetUrl);
|
|
713
|
+
const license = await verifyCliLicense({ domain: state.domain, token: authToken });
|
|
653
714
|
const catFilter = (args.category || "").toLowerCase();
|
|
654
715
|
const statusFilter = (args.status || "").toLowerCase();
|
|
655
716
|
const minDa = Number(args.min_da) || 0;
|
|
656
717
|
|
|
718
|
+
const maxAllowed = license.valid ? Number(args.limit) || 25 : 5;
|
|
719
|
+
|
|
657
720
|
const records = await mcpDirectories.queryWithSupersetCache({
|
|
658
721
|
sessionId,
|
|
659
722
|
cacheKey: `dirs:${state.domain}`,
|
|
@@ -662,20 +725,34 @@ export async function executeMcpToolCall(name, args = {}, sessionId = "mcp-stdio
|
|
|
662
725
|
(!catFilter || d.category.toLowerCase().includes(catFilter)) &&
|
|
663
726
|
(!statusFilter || d.status.toLowerCase().includes(statusFilter)) &&
|
|
664
727
|
d.dr >= minDa,
|
|
665
|
-
limit:
|
|
728
|
+
limit: maxAllowed,
|
|
666
729
|
bypassCache: Boolean(args.bypass_cache)
|
|
667
730
|
});
|
|
668
731
|
|
|
669
|
-
|
|
732
|
+
const compacted = mcpDirectories.compactList({
|
|
670
733
|
sessionId,
|
|
671
734
|
records,
|
|
672
735
|
bypassCache: Boolean(args.bypass_cache)
|
|
673
736
|
});
|
|
737
|
+
|
|
738
|
+
if (!license.valid) {
|
|
739
|
+
return {
|
|
740
|
+
...compacted,
|
|
741
|
+
_license_status: "PREVIEW_ONLY_5_OF_85",
|
|
742
|
+
_locked_directories: 80,
|
|
743
|
+
_checkout_url: license.checkoutUrl,
|
|
744
|
+
_unlock_instructions: "Purchase a $29 Launch Pass and run: npx git-paid login <pb_live_key>"
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
return compacted;
|
|
674
748
|
}
|
|
675
749
|
|
|
676
750
|
if (name === "postbuild_pseo") {
|
|
677
|
-
const targetUrl = args.url || "https://postbuild.dev";
|
|
751
|
+
const targetUrl = args.url || "https://www.postbuild.dev";
|
|
752
|
+
const license = await verifyCliLicense({ domain: targetUrl.replace(/^https?:\/\//, ""), token: authToken });
|
|
678
753
|
const intentFilter = (args.intent || "").toLowerCase();
|
|
754
|
+
const maxAllowed = license.valid ? 25 : 3;
|
|
755
|
+
|
|
679
756
|
const records = await mcpPseo.queryWithSupersetCache({
|
|
680
757
|
sessionId,
|
|
681
758
|
cacheKey: `pseo:${targetUrl}`,
|
|
@@ -688,20 +765,43 @@ export async function executeMcpToolCall(name, args = {}, sessionId = "mcp-stdio
|
|
|
688
765
|
}));
|
|
689
766
|
},
|
|
690
767
|
filter: (p) => !intentFilter || p.intent.toLowerCase().includes(intentFilter),
|
|
691
|
-
limit:
|
|
768
|
+
limit: maxAllowed,
|
|
692
769
|
bypassCache: Boolean(args.bypass_cache)
|
|
693
770
|
});
|
|
694
771
|
|
|
695
|
-
|
|
772
|
+
const compacted = mcpPseo.compactList({
|
|
696
773
|
sessionId,
|
|
697
774
|
records,
|
|
698
775
|
bypassCache: Boolean(args.bypass_cache)
|
|
699
776
|
});
|
|
777
|
+
|
|
778
|
+
if (!license.valid) {
|
|
779
|
+
return {
|
|
780
|
+
...compacted,
|
|
781
|
+
_license_status: "PREVIEW_ONLY_3_OF_25",
|
|
782
|
+
_locked_routes: 22,
|
|
783
|
+
_checkout_url: license.checkoutUrl,
|
|
784
|
+
_unlock_instructions: "Purchase a $29 Launch Pass and run: npx git-paid login <pb_live_key>"
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
return compacted;
|
|
700
788
|
}
|
|
701
789
|
|
|
702
790
|
if (name === "postbuild_dispatch") {
|
|
703
|
-
const targetUrl = args.url || "https://postbuild.dev";
|
|
791
|
+
const targetUrl = args.url || "https://www.postbuild.dev";
|
|
704
792
|
const state = getDomainDirectories(targetUrl);
|
|
793
|
+
const license = await verifyCliLicense({ domain: state.domain, token: authToken });
|
|
794
|
+
if (!license.valid) {
|
|
795
|
+
return {
|
|
796
|
+
isError: true,
|
|
797
|
+
status: "PRO_LICENSE_REQUIRED",
|
|
798
|
+
domain: state.domain,
|
|
799
|
+
checkoutUrl: license.checkoutUrl,
|
|
800
|
+
message: license.message,
|
|
801
|
+
loginCommand: "npx git-paid login <your-pb_live_key>"
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
705
805
|
const dryRun = args.dry_run !== undefined ? Boolean(args.dry_run) : true;
|
|
706
806
|
const targetIds = Array.isArray(args.directory_ids) ? args.directory_ids : [];
|
|
707
807
|
|
|
@@ -748,7 +848,7 @@ export async function executeMcpToolCall(name, args = {}, sessionId = "mcp-stdio
|
|
|
748
848
|
state.updated_at = nowIso;
|
|
749
849
|
for (const d of state.directories) {
|
|
750
850
|
if (!targetIds.length || targetIds.includes(d.id)) {
|
|
751
|
-
d.status = d.autoSubmit ? "
|
|
851
|
+
d.status = d.autoSubmit ? "indexnow_pinged_form_ready" : "prefilled_ready";
|
|
752
852
|
d.version_etag = nextVer;
|
|
753
853
|
d.updated_at = nowIso;
|
|
754
854
|
}
|
|
@@ -765,14 +865,23 @@ export async function executeMcpToolCall(name, args = {}, sessionId = "mcp-stdio
|
|
|
765
865
|
}
|
|
766
866
|
|
|
767
867
|
if (name === "postbuild_inbox") {
|
|
768
|
-
const
|
|
769
|
-
if (
|
|
770
|
-
|
|
868
|
+
const license = await verifyCliLicense({ domain: "postbuild.dev", token: authToken });
|
|
869
|
+
if (!license.valid) {
|
|
870
|
+
return {
|
|
871
|
+
isError: true,
|
|
872
|
+
status: "PRO_LICENSE_REQUIRED",
|
|
873
|
+
checkoutUrl: license.checkoutUrl,
|
|
874
|
+
message: license.message,
|
|
875
|
+
loginCommand: "npx git-paid login <your-pb_live_key>"
|
|
876
|
+
};
|
|
771
877
|
}
|
|
772
|
-
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
|
|
878
|
+
|
|
879
|
+
const inboxAddr = args.inbox || license.inbox || getBotInboxForDomain(license.domain || "postbuild.dev", license.email);
|
|
880
|
+
const autoVerify = Boolean(args.auto_verify);
|
|
881
|
+
const result = await autoVerifyInboxLinks(inboxAddr, {
|
|
882
|
+
autoVerify,
|
|
883
|
+
bearerToken: license.token
|
|
884
|
+
});
|
|
776
885
|
|
|
777
886
|
const compacted = mcpInbox.compactList({
|
|
778
887
|
sessionId,
|
|
@@ -789,7 +898,19 @@ export async function executeMcpToolCall(name, args = {}, sessionId = "mcp-stdio
|
|
|
789
898
|
}
|
|
790
899
|
|
|
791
900
|
if (name === "postbuild_fix") {
|
|
792
|
-
const report = await scanTargetUrl(args.url || "https://postbuild.dev");
|
|
901
|
+
const report = await scanTargetUrl(args.url || "https://www.postbuild.dev");
|
|
902
|
+
const license = await verifyCliLicense({ domain: report.domain, token: authToken });
|
|
903
|
+
if (!license.valid) {
|
|
904
|
+
return {
|
|
905
|
+
isError: true,
|
|
906
|
+
status: "PRO_LICENSE_REQUIRED",
|
|
907
|
+
domain: report.domain,
|
|
908
|
+
checkoutUrl: license.checkoutUrl,
|
|
909
|
+
message: license.message,
|
|
910
|
+
loginCommand: "npx git-paid login <your-pb_live_key>"
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
|
|
793
914
|
const fixRes = applyLocalSeoFixes(report, {
|
|
794
915
|
cwd: args.cwd || process.cwd(),
|
|
795
916
|
overwritePublic: true
|
|
@@ -798,10 +919,22 @@ export async function executeMcpToolCall(name, args = {}, sessionId = "mcp-stdio
|
|
|
798
919
|
}
|
|
799
920
|
|
|
800
921
|
if (name === "postbuild_vault_sync") {
|
|
922
|
+
const license = await verifyCliLicense({ domain: "postbuild.dev", token: authToken });
|
|
923
|
+
if (!license.valid) {
|
|
924
|
+
return {
|
|
925
|
+
isError: true,
|
|
926
|
+
status: "PRO_LICENSE_REQUIRED",
|
|
927
|
+
checkoutUrl: license.checkoutUrl,
|
|
928
|
+
message: license.message,
|
|
929
|
+
loginCommand: "npx git-paid login <your-pb_live_key>"
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
|
|
801
933
|
return await syncPostbuildToVault({
|
|
802
|
-
vaultPath: args.vault_path ||
|
|
803
|
-
urls: Array.isArray(args.urls) && args.urls.length ? args.urls : ["https://postbuild.dev"
|
|
804
|
-
sessionId
|
|
934
|
+
vaultPath: args.vault_path || process.cwd(),
|
|
935
|
+
urls: Array.isArray(args.urls) && args.urls.length ? args.urls : ["https://www.postbuild.dev"],
|
|
936
|
+
sessionId,
|
|
937
|
+
bearerToken: license.token
|
|
805
938
|
});
|
|
806
939
|
}
|
|
807
940
|
|