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/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 = "postbuild-bot@postbuild.dev") {
116
- const res = await listInboxMessages(inboxAddress);
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 || null,
126
- autoVerified: false,
127
- httpStatus: null,
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 controller = new AbortController();
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
- redirect: "follow",
140
- signal: controller.signal,
159
+ timeoutMs: 4500,
160
+ allowedDomains: ALLOWED_VERIFY_DOMAINS,
161
+ lookupFn: options.lookupFn,
141
162
  headers: {
142
- "User-Agent": "Postbuild-Autonomous-Verifier/2.1 (+https://postbuild.dev)"
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 = m.primaryLink.includes("postbuild.dev") || m.primaryLink.includes("confirm");
151
- entry.httpStatus = entry.autoVerified ? 200 : 0;
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.filter((x) => x.otpCode).map((x) => ({ from: x.from, otp: x.otpCode })),
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 = "C:\\Users\\Bennett\\Bennett Labs",
246
- urls = ["https://postbuild.dev", "https://payarity.com"],
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
- const currentInbox = await listInboxMessages(report.botEmail);
256
- if (currentInbox.count === 0) {
257
- await sendTestVerificationEmail(report.botEmail, report.domain);
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 || "849204",
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 (fs.existsSync(vaultPath)) {
316
- const metaDir = path.join(vaultPath, "_meta");
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(vaultPath, "_dashboards");
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
- vaultPath,
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 Cockpit
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 COCKPIT ────────────────────────────────────────────
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 &amp; SEO Command Center</div>
395
- <div class="hero-subtitle">Managed autonomously by Gemini × Postbuild MCP (<code>npx postbuild mcp</code>)</div>
446
+ <div class="hero-title">Postbuild Launch &amp; 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 &amp; 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} Auto-Verified) · Latest OTP: <code>\${p.latestOtp}</code>
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 postbuild init-agent`) ──────────
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 Dispatch, @postbuild.dev Inbox Auto-Verifier, and Obsidian VaultOS Sync powered by Postbuild MCP (postbuild-mcp v2.1.0).
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 paid\`) Agent Skill
536
+ # Postbuild (\`git-paid\`) Agent Skill
485
537
 
486
- Use the \`postbuild\` MCP server (\`npx postbuild mcp\`) or CLI (\`npx postbuild\`) to audit, fix, monetize, and launch any web project.
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", "postbuild", "mcp"]
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 (1ms subset filtering) + Watermark Context Residuals (\`unchanged@vN\`, -75% to -90% tokens).
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\` — Dispatch a project to directories protected by Two-Phase Cryptographic OCC (\`dry_run: true\` issues \`commit_token: "ct_..."\`; \`dry_run: false\` commits).
505
- 5. \`postbuild_inbox\` — Read \`<slug>-bot@postbuild.dev\` with Untrusted Content Spotlighting and automatically click/confirm directory verification links (\`auto_verify: true\`).
506
- 6. \`postbuild_vault_sync\` — Sync all live readiness scores, inbox OTPs, MCP token savings, and Buyer Radar threads directly into an Obsidian VaultOS dashboard (\`_meta/postbuild-telemetry.json\` & \`_dashboards/Postbuild Launch.md\`).
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", "postbuild", "mcp"]
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 postbuild mcp`) ───────────────────
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>-bot@postbuild.dev verification inbox.",
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) for >75% token reduction.",
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
- "Dispatch a domain across the 85 directories 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.",
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 (omit for all auto-dispatch directories)"
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>-bot@postbuild.dev verification inbox with Untrusted Content Spotlighting. Set auto_verify: true to have the agent automatically click confirmation links and extract 6-digit OTPs.",
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 (e.g. postbuild-bot@postbuild.dev)" },
596
- auto_verify: { type: "boolean", description: "Automatically follow confirmation links (default: true)" },
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
- "Sync live Postbuild SEO scores, 85-directory status, inbox OTPs, Buyer Radar threads, and MCP token telemetry into an Obsidian VaultOS dashboard (_meta/postbuild-telemetry.json & _dashboards/Postbuild Launch.md).",
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: "Absolute path to Obsidian vault (default: C:\\Users\\Bennett\\Bennett Labs)"
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: Number(args.limit) || 25,
728
+ limit: maxAllowed,
666
729
  bypassCache: Boolean(args.bypass_cache)
667
730
  });
668
731
 
669
- return mcpDirectories.compactList({
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: 25,
768
+ limit: maxAllowed,
692
769
  bypassCache: Boolean(args.bypass_cache)
693
770
  });
694
771
 
695
- return mcpPseo.compactList({
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 ? "dispatched" : "guided_ready";
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 inboxAddr = args.inbox || "postbuild-bot@postbuild.dev";
769
- if (args.send_test) {
770
- await sendTestVerificationEmail(inboxAddr, "postbuild.dev");
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
- const autoVerify = args.auto_verify !== undefined ? Boolean(args.auto_verify) : true;
773
- const result = autoVerify
774
- ? await autoVerifyInboxLinks(inboxAddr)
775
- : await listInboxMessages(inboxAddr);
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 || "C:\\Users\\Bennett\\Bennett Labs",
803
- urls: Array.isArray(args.urls) && args.urls.length ? args.urls : ["https://postbuild.dev", "https://payarity.com"],
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