backlinkflow 0.1.1 → 0.1.2
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/index.js +5 -5
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ async function cmdList() {
|
|
|
29
29
|
const all = loadDirectories();
|
|
30
30
|
const filtered = cat ? all.filter((d) => d.category === cat) : all;
|
|
31
31
|
console.log(`
|
|
32
|
-
|
|
32
|
+
BacklinkFlow directory database: ${all.length} sites
|
|
33
33
|
`);
|
|
34
34
|
for (const [i, d] of filtered.entries()) {
|
|
35
35
|
if (LIMIT && i >= LIMIT) break;
|
|
@@ -57,7 +57,7 @@ async function cmdSubmit() {
|
|
|
57
57
|
process.exit(1);
|
|
58
58
|
}
|
|
59
59
|
const cfg = loadConfig(CONFIG_FILE);
|
|
60
|
-
console.log("\
|
|
60
|
+
console.log("\nBacklinkFlow submit");
|
|
61
61
|
console.log("\u2500".repeat(50));
|
|
62
62
|
printConfigSummary(cfg);
|
|
63
63
|
if (!hasAiConfig(cfg)) {
|
|
@@ -151,7 +151,7 @@ async function cmdPayload() {
|
|
|
151
151
|
}
|
|
152
152
|
function cmdStatus() {
|
|
153
153
|
const s = trackerSummary();
|
|
154
|
-
console.log("\
|
|
154
|
+
console.log("\nBacklinkFlow tracker");
|
|
155
155
|
console.log("\u2500".repeat(50));
|
|
156
156
|
console.log(` Total records: ${s.total}`);
|
|
157
157
|
for (const [k, v] of Object.entries(s.byStatus)) console.log(` ${k}: ${v}`);
|
|
@@ -169,7 +169,7 @@ function cmdReport() {
|
|
|
169
169
|
}
|
|
170
170
|
function cmdStats() {
|
|
171
171
|
const s = dbStats();
|
|
172
|
-
console.log("\
|
|
172
|
+
console.log("\nBacklinkFlow database stats");
|
|
173
173
|
console.log("\u2500".repeat(50));
|
|
174
174
|
console.log(` Total directories: ${s.total}`);
|
|
175
175
|
for (const [k, v] of Object.entries(s.byCategory)) console.log(` ${k}: ${v}`);
|
|
@@ -267,7 +267,7 @@ Run 'backlinkflow' with: list | search | submit | payload | status | report | st
|
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
main().catch((err) => {
|
|
270
|
-
console.error("
|
|
270
|
+
console.error("BacklinkFlow error:", err);
|
|
271
271
|
process.exit(1);
|
|
272
272
|
});
|
|
273
273
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env node\n/**\n * LinkFlow \u2014 zero-cost backlink & directory submission engine.\n *\n * Commands:\n * linkflow list [--category X] [--limit N] List directories in the database\n * linkflow search <query> Search directories\n * linkflow submit <site-url> [--dry-run] [--limit N] [--category X] Generate payloads + submit plan\n * linkflow payload <site-url> [--directory X] Generate AI-tailored payload only (no submission)\n * linkflow status Show submission tracker summary\n * linkflow report Regenerate report from tracker\n * linkflow stats Database stats\n * linkflow db:review Flag DB quality issues (dead links, homepage-as-submit)\n * linkflow db:regenerate Rebuild DB from source lists (scripts/regenerate-db.py)\n * linkflow init Print config template\n */\nimport fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport { loadDirectories, searchDirectories, categories, dbStats } from './database.js';\nimport { loadConfig, hasAiConfig, printConfigSummary } from './config.js';\nimport { resetAiCallCount, getAiCallCount } from './ai.js';\nimport { generatePayload } from './payload.js';\nimport { loadTracker, alreadySubmitted, recordSubmission, trackerSummary } from './tracker.js';\nimport { writeReport, reportPath } from './report.js';\nimport { submitOne } from './engine/submit.js';\n\nconst rawArgs = process.argv.slice(2);\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst VERB = rawArgs[0] && !rawArgs[0].startsWith('--') ? rawArgs[0] : 'list';\nconst VERB_ARG = rawArgs[1] && !rawArgs[1].startsWith('--') ? rawArgs[1] : null;\n\nconst flag = (name: string): string | undefined => {\n const i = rawArgs.indexOf(name);\n return i !== -1 ? rawArgs[i + 1] : undefined;\n};\nconst has = (name: string): boolean => rawArgs.includes(name);\n\nconst DRY_RUN = has('--dry-run');\nconst LIMIT = parseInt(flag('--limit') || '0') || 0;\nconst CONFIG_FILE = flag('--config');\n\nfunction formatDir(d: any, i: number): string {\n return `${String(i + 1).padStart(3)}. ${d.name.padEnd(28)} [${d.category.padEnd(22)}] auto=${d.auto} ${d.dr ? `DR${d.dr}` : ''} ${d.status && d.status !== 'active' ? `(${d.status})` : ''}`;\n}\n\nasync function cmdList(): Promise<void> {\n const cat = flag('--category');\n const all = loadDirectories();\n const filtered = cat ? all.filter((d) => d.category === cat) : all;\n console.log(`\\nLinkFlow directory database: ${all.length} sites\\n`);\n for (const [i, d] of filtered.entries()) {\n if (LIMIT && i >= LIMIT) break;\n console.log(formatDir(d, i));\n }\n console.log(`\\n Categories: ${categories().join(', ')}`);\n}\n\nasync function cmdSearch(): Promise<void> {\n const q = VERB_ARG || '';\n const results = searchDirectories(q);\n console.log(`\\nSearch \"${q}\": ${results.length} matches\\n`);\n for (const [i, d] of results.entries()) {\n if (LIMIT && i >= LIMIT) break;\n console.log(formatDir(d, i));\n console.log(` ${d.submitUrl}`);\n }\n}\n\nasync function cmdSubmit(): Promise<void> {\n const siteUrl = VERB_ARG;\n if (!siteUrl) {\n console.log('Usage: backlinkflow submit <site-url> [--dry-run] [--limit N] [--category X] [--go]');\n process.exit(1);\n }\n const cfg = loadConfig(CONFIG_FILE);\n console.log('\\nLinkFlow submit');\n console.log('\u2500'.repeat(50));\n printConfigSummary(cfg);\n\n if (!hasAiConfig(cfg)) {\n console.log(' \u26A0\uFE0F No AI config (.env.local AI_BASE_URL/AI_API_KEY) \u2014 using template payloads.');\n }\n\n const cat = flag('--category');\n const targets = loadDirectories().filter((d) => {\n if (cat && d.category !== cat) return false;\n return d.auto === 'yes' || d.auto === 'manual'; // skip dead/paid-only\n });\n const selected = LIMIT ? targets.slice(0, LIMIT) : targets;\n\n console.log(`\\n Target directories: ${selected.length} (auto+manual, ${cat || 'all categories'})\\n`);\n resetAiCallCount();\n\n const records = loadTracker();\n let submitted = 0, skipped = 0, failed = 0;\n\n for (const [i, dir] of selected.entries()) {\n if (alreadySubmitted(siteUrl, dir.name)) {\n console.log(` [${i + 1}/${selected.length}] \u23ED\uFE0F ${dir.name} \u2014 already submitted (tracked)`);\n skipped++;\n continue;\n }\n const payload = await generatePayload(dir, cfg);\n console.log(` [${i + 1}/${selected.length}] \uD83D\uDE80 ${dir.name}`);\n console.log(` tagline: ${payload.tagline.slice(0, 80)}`);\n console.log(` submit: ${dir.submitUrl}`);\n\n if (has('--go')) {\n // REAL automation via Playwright\n const result = await submitOne(dir, payload, {\n siteUrl,\n proofDir: '.linkflow/proofs',\n });\n console.log(` \u2192 ${result.status}: ${result.note || ''}${result.proof ? ` (proof: ${result.proof})` : ''}`);\n if (result.status === 'submitted') submitted++;\n else if (result.status === 'failed') failed++;\n\n // pacing between submissions (respect per-day limit)\n const perDay = cfg.pacing?.perDay ?? 10;\n if (i + 1 >= perDay && i + 1 < selected.length) {\n console.log(` \u23F8\uFE0F Daily pacing limit (${perDay}) reached \u2014 stopping.`);\n break;\n }\n if (i + 1 < selected.length) {\n const pause = (cfg.pacing?.minSeconds ?? 60) * 1000;\n console.log(` \u23F3 pacing ${pause / 1000}s before next\u2026`);\n await new Promise((r) => setTimeout(r, pause));\n }\n } else {\n // plan-only mode (v0.1 behavior)\n console.log(` desc: ${payload.description.slice(0, 100)}`);\n if (!DRY_RUN) {\n recordSubmission({\n site: siteUrl,\n directory: dir.name,\n status: 'pending',\n submittedAt: new Date().toISOString(),\n url: dir.submitUrl,\n notes: 'planned \u2014 run with --go for automation',\n });\n submitted++;\n }\n }\n }\n\n console.log(`\\n Done: ${submitted} submitted/planned, ${skipped} skipped, ${failed} failed. AI calls: ${getAiCallCount()}`);\n\n if (!DRY_RUN) {\n const all = loadTracker();\n writeReport(siteUrl, all.filter((r) => r.site === siteUrl));\n const { md } = reportPath();\n console.log(` Report: ${md}`);\n } else {\n console.log(' (dry-run \u2014 nothing recorded)');\n }\n}\n\nasync function cmdPayload(): Promise<void> {\n const siteUrl = VERB_ARG;\n if (!siteUrl) {\n console.log('Usage: backlinkflow payload <site-url> [--directory X]');\n process.exit(1);\n }\n const cfg = loadConfig(CONFIG_FILE);\n const dirName = flag('--directory');\n const dirs = dirName ? loadDirectories().filter((d) => d.name.toLowerCase().includes(dirName.toLowerCase())) : loadDirectories();\n if (!dirs.length) {\n console.log(' No matching directory.');\n return;\n }\n for (const dir of dirs.slice(0, 3)) {\n const payload = await generatePayload(dir, cfg);\n console.log(`\\n=== ${dir.name} ===`);\n console.log(JSON.stringify(payload, null, 2));\n }\n}\n\nfunction cmdStatus(): void {\n const s = trackerSummary();\n console.log('\\nLinkFlow tracker');\n console.log('\u2500'.repeat(50));\n console.log(` Total records: ${s.total}`);\n for (const [k, v] of Object.entries(s.byStatus)) console.log(` ${k}: ${v}`);\n if (s.sites.length) console.log(` Sites: ${s.sites.join(', ')}`);\n}\n\nfunction cmdReport(): void {\n const cfg = loadConfig();\n const site = VERB_ARG || cfg.siteUrl || 'all';\n const all = loadTracker();\n const filtered = site === 'all' ? all : all.filter((r) => r.site === site);\n writeReport(site, filtered);\n const { md } = reportPath();\n console.log(`\\n Report written: ${md}`);\n}\n\nfunction cmdStats(): void {\n const s = dbStats();\n console.log('\\nLinkFlow database stats');\n console.log('\u2500'.repeat(50));\n console.log(` Total directories: ${s.total}`);\n for (const [k, v] of Object.entries(s.byCategory)) console.log(` ${k}: ${v}`);\n console.log(` Auto-submittable: ${s.auto}`);\n}\n\nfunction cmdInit(): void {\n const tpl = {\n siteName: 'My Product',\n siteUrl: 'https://example.com',\n siteDescription: 'A short, honest description of what it is and who it is for.',\n tags: ['saas', 'devtools'],\n contentDomain: 'SaaS product',\n writingSample: 'Paste 2-3 sentences in your site voice here.',\n ai: {\n provider: 'openai',\n baseUrl: 'http://192.168.0.254:20128/v1',\n apiKey: 'your-omniroute-key',\n model: 'auto/best-free',\n maxCallsPerRun: 20,\n },\n pacing: { minSeconds: 60, perDay: 10 },\n };\n const p = path.join(process.cwd(), 'linkflow.config.json');\n if (fs.existsSync(p)) {\n console.log(` ${p} already exists \u2014 not overwriting.`);\n } else {\n fs.writeFileSync(p, JSON.stringify(tpl, null, 2));\n console.log(` Created ${p}`);\n }\n}\n\nasync function cmdDbReview(): Promise<void> {\n const { execSync } = await import('child_process');\n const script = path.join(__dirname, '..', 'scripts', 'review-db.py');\n if (!fs.existsSync(script)) {\n console.log(' review-db.py not found \u2014 run from repo root.');\n return;\n }\n try {\n const out = execSync(`python3 \"${script}\"`, { encoding: 'utf8' });\n console.log(out);\n } catch (err) {\n console.log(' Review failed:', (err as Error).message);\n }\n}\n\nasync function cmdDbRegenerate(): Promise<void> {\n const { execSync } = await import('child_process');\n const script = path.join(__dirname, '..', 'scripts', 'regenerate-db.py');\n if (!fs.existsSync(script)) {\n console.log(' regenerate-db.py not found \u2014 run from repo root.');\n return;\n }\n try {\n const out = execSync(`python3 \"${script}\"`, { encoding: 'utf8' });\n console.log(out);\n } catch (err) {\n console.log(' Regenerate failed:', (err as Error).message);\n }\n}\n\nasync function main(): Promise<void> {\n switch (VERB) {\n case 'list': await cmdList(); break;\n case 'search': await cmdSearch(); break;\n case 'submit': await cmdSubmit(); break;\n case 'payload': await cmdPayload(); break;\n case 'status': cmdStatus(); break;\n case 'report': cmdReport(); break;\n case 'stats': cmdStats(); break;\n case 'db:review': await cmdDbReview(); break;\n case 'db:regenerate': await cmdDbRegenerate(); break;\n case 'init': cmdInit(); break;\n default:\n console.log(`Unknown command: ${VERB}\\nRun 'backlinkflow' with: list | search | submit | payload | status | report | stats | db:review | db:regenerate | init`);\n process.exit(1);\n }\n}\n\nmain().catch((err) => {\n console.error('LinkFlow error:', err);\n process.exit(1);\n});\n"],
|
|
5
|
-
"mappings": ";AAgBA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB,mBAAmB,YAAY,eAAe;AACxE,SAAS,YAAY,aAAa,0BAA0B;AAC5D,SAAS,kBAAkB,sBAAsB;AACjD,SAAS,uBAAuB;AAChC,SAAS,aAAa,kBAAkB,kBAAkB,sBAAsB;AAChF,SAAS,aAAa,kBAAkB;AACxC,SAAS,iBAAiB;AAE1B,MAAM,UAAU,QAAQ,KAAK,MAAM,CAAC;AACpC,MAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC7D,MAAM,OAAO,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,WAAW,IAAI,IAAI,QAAQ,CAAC,IAAI;AACvE,MAAM,WAAW,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,WAAW,IAAI,IAAI,QAAQ,CAAC,IAAI;AAE3E,MAAM,OAAO,CAAC,SAAqC;AACjD,QAAM,IAAI,QAAQ,QAAQ,IAAI;AAC9B,SAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,IAAI;AACrC;AACA,MAAM,MAAM,CAAC,SAA0B,QAAQ,SAAS,IAAI;AAE5D,MAAM,UAAU,IAAI,WAAW;AAC/B,MAAM,QAAQ,SAAS,KAAK,SAAS,KAAK,GAAG,KAAK;AAClD,MAAM,cAAc,KAAK,UAAU;AAEnC,SAAS,UAAU,GAAQ,GAAmB;AAC5C,SAAO,GAAG,OAAO,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC,KAAK,EAAE,KAAK,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,CAAC,UAAU,EAAE,IAAI,IAAI,EAAE,KAAK,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,WAAW,IAAI,EAAE,MAAM,MAAM,EAAE;AAC5L;AAEA,eAAe,UAAyB;AACtC,QAAM,MAAM,KAAK,YAAY;AAC7B,QAAM,MAAM,gBAAgB;AAC5B,QAAM,WAAW,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;AAC/D,UAAQ,IAAI;AAAA
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n/**\n * LinkFlow \u2014 zero-cost backlink & directory submission engine.\n *\n * Commands:\n * linkflow list [--category X] [--limit N] List directories in the database\n * linkflow search <query> Search directories\n * linkflow submit <site-url> [--dry-run] [--limit N] [--category X] Generate payloads + submit plan\n * linkflow payload <site-url> [--directory X] Generate AI-tailored payload only (no submission)\n * linkflow status Show submission tracker summary\n * linkflow report Regenerate report from tracker\n * linkflow stats Database stats\n * linkflow db:review Flag DB quality issues (dead links, homepage-as-submit)\n * linkflow db:regenerate Rebuild DB from source lists (scripts/regenerate-db.py)\n * linkflow init Print config template\n */\nimport fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport { loadDirectories, searchDirectories, categories, dbStats } from './database.js';\nimport { loadConfig, hasAiConfig, printConfigSummary } from './config.js';\nimport { resetAiCallCount, getAiCallCount } from './ai.js';\nimport { generatePayload } from './payload.js';\nimport { loadTracker, alreadySubmitted, recordSubmission, trackerSummary } from './tracker.js';\nimport { writeReport, reportPath } from './report.js';\nimport { submitOne } from './engine/submit.js';\n\nconst rawArgs = process.argv.slice(2);\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst VERB = rawArgs[0] && !rawArgs[0].startsWith('--') ? rawArgs[0] : 'list';\nconst VERB_ARG = rawArgs[1] && !rawArgs[1].startsWith('--') ? rawArgs[1] : null;\n\nconst flag = (name: string): string | undefined => {\n const i = rawArgs.indexOf(name);\n return i !== -1 ? rawArgs[i + 1] : undefined;\n};\nconst has = (name: string): boolean => rawArgs.includes(name);\n\nconst DRY_RUN = has('--dry-run');\nconst LIMIT = parseInt(flag('--limit') || '0') || 0;\nconst CONFIG_FILE = flag('--config');\n\nfunction formatDir(d: any, i: number): string {\n return `${String(i + 1).padStart(3)}. ${d.name.padEnd(28)} [${d.category.padEnd(22)}] auto=${d.auto} ${d.dr ? `DR${d.dr}` : ''} ${d.status && d.status !== 'active' ? `(${d.status})` : ''}`;\n}\n\nasync function cmdList(): Promise<void> {\n const cat = flag('--category');\n const all = loadDirectories();\n const filtered = cat ? all.filter((d) => d.category === cat) : all;\n console.log(`\\nBacklinkFlow directory database: ${all.length} sites\\n`);\n for (const [i, d] of filtered.entries()) {\n if (LIMIT && i >= LIMIT) break;\n console.log(formatDir(d, i));\n }\n console.log(`\\n Categories: ${categories().join(', ')}`);\n}\n\nasync function cmdSearch(): Promise<void> {\n const q = VERB_ARG || '';\n const results = searchDirectories(q);\n console.log(`\\nSearch \"${q}\": ${results.length} matches\\n`);\n for (const [i, d] of results.entries()) {\n if (LIMIT && i >= LIMIT) break;\n console.log(formatDir(d, i));\n console.log(` ${d.submitUrl}`);\n }\n}\n\nasync function cmdSubmit(): Promise<void> {\n const siteUrl = VERB_ARG;\n if (!siteUrl) {\n console.log('Usage: backlinkflow submit <site-url> [--dry-run] [--limit N] [--category X] [--go]');\n process.exit(1);\n }\n const cfg = loadConfig(CONFIG_FILE);\n console.log('\\nBacklinkFlow submit');\n console.log('\u2500'.repeat(50));\n printConfigSummary(cfg);\n\n if (!hasAiConfig(cfg)) {\n console.log(' \u26A0\uFE0F No AI config (.env.local AI_BASE_URL/AI_API_KEY) \u2014 using template payloads.');\n }\n\n const cat = flag('--category');\n const targets = loadDirectories().filter((d) => {\n if (cat && d.category !== cat) return false;\n return d.auto === 'yes' || d.auto === 'manual'; // skip dead/paid-only\n });\n const selected = LIMIT ? targets.slice(0, LIMIT) : targets;\n\n console.log(`\\n Target directories: ${selected.length} (auto+manual, ${cat || 'all categories'})\\n`);\n resetAiCallCount();\n\n const records = loadTracker();\n let submitted = 0, skipped = 0, failed = 0;\n\n for (const [i, dir] of selected.entries()) {\n if (alreadySubmitted(siteUrl, dir.name)) {\n console.log(` [${i + 1}/${selected.length}] \u23ED\uFE0F ${dir.name} \u2014 already submitted (tracked)`);\n skipped++;\n continue;\n }\n const payload = await generatePayload(dir, cfg);\n console.log(` [${i + 1}/${selected.length}] \uD83D\uDE80 ${dir.name}`);\n console.log(` tagline: ${payload.tagline.slice(0, 80)}`);\n console.log(` submit: ${dir.submitUrl}`);\n\n if (has('--go')) {\n // REAL automation via Playwright\n const result = await submitOne(dir, payload, {\n siteUrl,\n proofDir: '.linkflow/proofs',\n });\n console.log(` \u2192 ${result.status}: ${result.note || ''}${result.proof ? ` (proof: ${result.proof})` : ''}`);\n if (result.status === 'submitted') submitted++;\n else if (result.status === 'failed') failed++;\n\n // pacing between submissions (respect per-day limit)\n const perDay = cfg.pacing?.perDay ?? 10;\n if (i + 1 >= perDay && i + 1 < selected.length) {\n console.log(` \u23F8\uFE0F Daily pacing limit (${perDay}) reached \u2014 stopping.`);\n break;\n }\n if (i + 1 < selected.length) {\n const pause = (cfg.pacing?.minSeconds ?? 60) * 1000;\n console.log(` \u23F3 pacing ${pause / 1000}s before next\u2026`);\n await new Promise((r) => setTimeout(r, pause));\n }\n } else {\n // plan-only mode (v0.1 behavior)\n console.log(` desc: ${payload.description.slice(0, 100)}`);\n if (!DRY_RUN) {\n recordSubmission({\n site: siteUrl,\n directory: dir.name,\n status: 'pending',\n submittedAt: new Date().toISOString(),\n url: dir.submitUrl,\n notes: 'planned \u2014 run with --go for automation',\n });\n submitted++;\n }\n }\n }\n\n console.log(`\\n Done: ${submitted} submitted/planned, ${skipped} skipped, ${failed} failed. AI calls: ${getAiCallCount()}`);\n\n if (!DRY_RUN) {\n const all = loadTracker();\n writeReport(siteUrl, all.filter((r) => r.site === siteUrl));\n const { md } = reportPath();\n console.log(` Report: ${md}`);\n } else {\n console.log(' (dry-run \u2014 nothing recorded)');\n }\n}\n\nasync function cmdPayload(): Promise<void> {\n const siteUrl = VERB_ARG;\n if (!siteUrl) {\n console.log('Usage: backlinkflow payload <site-url> [--directory X]');\n process.exit(1);\n }\n const cfg = loadConfig(CONFIG_FILE);\n const dirName = flag('--directory');\n const dirs = dirName ? loadDirectories().filter((d) => d.name.toLowerCase().includes(dirName.toLowerCase())) : loadDirectories();\n if (!dirs.length) {\n console.log(' No matching directory.');\n return;\n }\n for (const dir of dirs.slice(0, 3)) {\n const payload = await generatePayload(dir, cfg);\n console.log(`\\n=== ${dir.name} ===`);\n console.log(JSON.stringify(payload, null, 2));\n }\n}\n\nfunction cmdStatus(): void {\n const s = trackerSummary();\n console.log('\\nBacklinkFlow tracker');\n console.log('\u2500'.repeat(50));\n console.log(` Total records: ${s.total}`);\n for (const [k, v] of Object.entries(s.byStatus)) console.log(` ${k}: ${v}`);\n if (s.sites.length) console.log(` Sites: ${s.sites.join(', ')}`);\n}\n\nfunction cmdReport(): void {\n const cfg = loadConfig();\n const site = VERB_ARG || cfg.siteUrl || 'all';\n const all = loadTracker();\n const filtered = site === 'all' ? all : all.filter((r) => r.site === site);\n writeReport(site, filtered);\n const { md } = reportPath();\n console.log(`\\n Report written: ${md}`);\n}\n\nfunction cmdStats(): void {\n const s = dbStats();\n console.log('\\nBacklinkFlow database stats');\n console.log('\u2500'.repeat(50));\n console.log(` Total directories: ${s.total}`);\n for (const [k, v] of Object.entries(s.byCategory)) console.log(` ${k}: ${v}`);\n console.log(` Auto-submittable: ${s.auto}`);\n}\n\nfunction cmdInit(): void {\n const tpl = {\n siteName: 'My Product',\n siteUrl: 'https://example.com',\n siteDescription: 'A short, honest description of what it is and who it is for.',\n tags: ['saas', 'devtools'],\n contentDomain: 'SaaS product',\n writingSample: 'Paste 2-3 sentences in your site voice here.',\n ai: {\n provider: 'openai',\n baseUrl: 'http://192.168.0.254:20128/v1',\n apiKey: 'your-omniroute-key',\n model: 'auto/best-free',\n maxCallsPerRun: 20,\n },\n pacing: { minSeconds: 60, perDay: 10 },\n };\n const p = path.join(process.cwd(), 'linkflow.config.json');\n if (fs.existsSync(p)) {\n console.log(` ${p} already exists \u2014 not overwriting.`);\n } else {\n fs.writeFileSync(p, JSON.stringify(tpl, null, 2));\n console.log(` Created ${p}`);\n }\n}\n\nasync function cmdDbReview(): Promise<void> {\n const { execSync } = await import('child_process');\n const script = path.join(__dirname, '..', 'scripts', 'review-db.py');\n if (!fs.existsSync(script)) {\n console.log(' review-db.py not found \u2014 run from repo root.');\n return;\n }\n try {\n const out = execSync(`python3 \"${script}\"`, { encoding: 'utf8' });\n console.log(out);\n } catch (err) {\n console.log(' Review failed:', (err as Error).message);\n }\n}\n\nasync function cmdDbRegenerate(): Promise<void> {\n const { execSync } = await import('child_process');\n const script = path.join(__dirname, '..', 'scripts', 'regenerate-db.py');\n if (!fs.existsSync(script)) {\n console.log(' regenerate-db.py not found \u2014 run from repo root.');\n return;\n }\n try {\n const out = execSync(`python3 \"${script}\"`, { encoding: 'utf8' });\n console.log(out);\n } catch (err) {\n console.log(' Regenerate failed:', (err as Error).message);\n }\n}\n\nasync function main(): Promise<void> {\n switch (VERB) {\n case 'list': await cmdList(); break;\n case 'search': await cmdSearch(); break;\n case 'submit': await cmdSubmit(); break;\n case 'payload': await cmdPayload(); break;\n case 'status': cmdStatus(); break;\n case 'report': cmdReport(); break;\n case 'stats': cmdStats(); break;\n case 'db:review': await cmdDbReview(); break;\n case 'db:regenerate': await cmdDbRegenerate(); break;\n case 'init': cmdInit(); break;\n default:\n console.log(`Unknown command: ${VERB}\\nRun 'backlinkflow' with: list | search | submit | payload | status | report | stats | db:review | db:regenerate | init`);\n process.exit(1);\n }\n}\n\nmain().catch((err) => {\n console.error('BacklinkFlow error:', err);\n process.exit(1);\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backlinkflow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "LinkFlow — zero-cost backlink & directory submission engine: vetted directory database, AI-tailored submission payloads, tracker, and proof-of-submission reports.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|