backlinkflow 0.1.0 → 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/CREDITS.md +4 -4
- package/README.md +37 -37
- package/dist/index.js +8 -8
- package/dist/index.js.map +2 -2
- package/llms.txt +1 -1
- package/package.json +2 -2
package/CREDITS.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
#
|
|
1
|
+
# BacklinkFlow — Sources & Credits
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
BacklinkFlow's directory database (`data/directories.yaml`) is an independent,
|
|
4
4
|
deduplicated compilation built by merging factual directory listings (site
|
|
5
5
|
names + submission URLs) from the open-source projects below.
|
|
6
6
|
|
|
7
7
|
**What we take:** factual data only — site name, submission URL, and (where
|
|
8
8
|
the source provided it) domain authority / price model / status.
|
|
9
9
|
|
|
10
|
-
**What we write ourselves:** all descriptions/notes are
|
|
10
|
+
**What we write ourselves:** all descriptions/notes are BacklinkFlow's own
|
|
11
11
|
(never copied verbatim from sources), plus the `auto`, `lang`, `category`,
|
|
12
12
|
and `status` enrichment fields.
|
|
13
13
|
|
|
@@ -60,4 +60,4 @@ Also referenced for methodology (not data):
|
|
|
60
60
|
files in `scripts/sources/`. Delete a source file there → re-run
|
|
61
61
|
`linkflow db:regenerate` → that source's entries are gone.
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
BacklinkFlow itself is MIT (see LICENSE).
|
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# BacklinkFlow
|
|
4
4
|
|
|
5
5
|
**Zero-cost backlink & directory submission engine for indie hackers.**
|
|
6
6
|
|
|
7
7
|
[](./LICENSE)
|
|
8
8
|
[](./package.json)
|
|
9
|
-
[](#the-flow-series)
|
|
10
10
|
|
|
11
11
|
*1,123 vetted directories · AI-tailored submission copy · Playwright browser automation · proof-of-submission reports — no SaaS, no credits, no $79 upsell.*
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
> **TL;DR** — Submitator, ListingBott, BoringLaunch charge $29–499 to do what
|
|
17
|
+
> **TL;DR** — Submitator, ListingBott, BoringLaunch charge $29–499 to do what BacklinkFlow does for free: a curated directory list, AI-generated per-directory copy, browser automation, and a status report. The only thing you bring is your own AI endpoint (OmniRoute, OpenAI, or any OpenAI-compatible API). Live-verified. MIT licensed.
|
|
18
18
|
|
|
19
19
|
**The what:**
|
|
20
20
|
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
32
|
-
## Why
|
|
32
|
+
## Why BacklinkFlow?
|
|
33
33
|
|
|
34
34
|
Every paid directory-submission service is the same three things wrapped in a dashboard:
|
|
35
35
|
|
|
@@ -37,11 +37,11 @@ Every paid directory-submission service is the same three things wrapped in a da
|
|
|
37
37
|
2. **Per-directory copy** — this is a 5-line AI call
|
|
38
38
|
3. **A status report** — this is a JSON file + a markdown table
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
BacklinkFlow gives you all three, plus actual browser automation for sites with open forms. The moat is honesty: 876 directories live-verified, 221 marked dead with reason, 26 marked paid. No padding with self-owned link farms. No "AI" that means "Army of Indians."
|
|
41
41
|
|
|
42
42
|
**vs competitors:**
|
|
43
43
|
|
|
44
|
-
| |
|
|
44
|
+
| | BacklinkFlow | Submitator | ListingBott | BoringLaunch |
|
|
45
45
|
|---|---|---|---|---|
|
|
46
46
|
| Directories | 1,123 (876 alive) | ~150 | ~250 | ~200 |
|
|
47
47
|
| Cost | $0 | $29–79 | $499 | $249 |
|
|
@@ -58,7 +58,7 @@ LinkFlow gives you all three, plus actual browser automation for sites with open
|
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
60
|
# from source
|
|
61
|
-
git clone https://github.com/imsankz/
|
|
61
|
+
git clone https://github.com/imsankz/backlinkflow.git
|
|
62
62
|
cd linkflow && npm install && npm run build
|
|
63
63
|
|
|
64
64
|
# or (when published)
|
|
@@ -69,41 +69,41 @@ npm install -g linkflow
|
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
71
|
# 1. Create config
|
|
72
|
-
|
|
72
|
+
backlinkflow init
|
|
73
73
|
|
|
74
74
|
# 2. Edit linkflow.config.json + .env.local (AI endpoint)
|
|
75
75
|
# AI_BASE_URL=http://192.168.0.254:20128/v1 (OmniRoute)
|
|
76
76
|
# AI_API_KEY=... AI_MODEL=auto/best-free
|
|
77
77
|
|
|
78
78
|
# 3. Explore the database
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
backlinkflow stats
|
|
80
|
+
backlinkflow list --category startup --limit 10
|
|
81
|
+
backlinkflow search "product hunt"
|
|
82
82
|
|
|
83
83
|
# 4. Generate AI-tailored submission copy (no submission)
|
|
84
|
-
|
|
84
|
+
backlinkflow payload https://yoursite.com --directory "Future Tools"
|
|
85
85
|
|
|
86
86
|
# 5. Plan submissions (dry-run — nothing recorded)
|
|
87
|
-
|
|
87
|
+
backlinkflow submit https://yoursite.com --dry-run --limit 10
|
|
88
88
|
|
|
89
89
|
# 6. Real automation — Playwright browser submits for you
|
|
90
|
-
|
|
90
|
+
backlinkflow submit https://yoursite.com --go --limit 10
|
|
91
91
|
|
|
92
92
|
# 7. Track + report
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
backlinkflow status
|
|
94
|
+
backlinkflow report # → .linkflow/report.md (Submitator-style proof)
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
## Multiple sites, one tool
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
BacklinkFlow is generic — bring your own site config. Each product = one config file:
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
102
|
# SaaS product
|
|
103
|
-
|
|
103
|
+
backlinkflow submit https://kreatorlane.com --config examples/linkflow.config.kreatorlane.json --category startup --go --limit 5
|
|
104
104
|
|
|
105
105
|
# Travel blog
|
|
106
|
-
|
|
106
|
+
backlinkflow submit https://chasingwhereabouts.com --config examples/linkflow.config.chasingwhereabouts.json --category travel --go --limit 5
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
No hardcoded sites. Examples in `examples/`.
|
|
@@ -114,16 +114,16 @@ No hardcoded sites. Examples in `examples/`.
|
|
|
114
114
|
|
|
115
115
|
| Command | Description |
|
|
116
116
|
|---|---|
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
117
|
+
| `backlinkflow list [--category X] [--limit N]` | List directories in the database |
|
|
118
|
+
| `backlinkflow search <query>` | Search directories by name/notes/URL |
|
|
119
|
+
| `backlinkflow submit <url> [--dry-run] [--limit N] [--category X] [--go] [--config file.json]` | Generate payloads + submit. `--go` = real Playwright automation |
|
|
120
|
+
| `backlinkflow payload <url> [--directory X]` | AI-tailored submission copy only |
|
|
121
|
+
| `backlinkflow status` | Tracker summary (submitted/pending/failed) |
|
|
122
|
+
| `backlinkflow report` | Regenerate proof-of-submission report |
|
|
123
|
+
| `backlinkflow stats` | Database stats (counts by category, alive/dead) |
|
|
124
|
+
| `backlinkflow db:review` | Flag DB quality issues (dead links, homepage-as-submit) |
|
|
125
|
+
| `backlinkflow db:regenerate` | Rebuild DB from source lists |
|
|
126
|
+
| `backlinkflow init` | Write config template |
|
|
127
127
|
|
|
128
128
|
---
|
|
129
129
|
|
|
@@ -201,7 +201,7 @@ The AI client is **SSE-tolerant** — works with providers that stream even when
|
|
|
201
201
|
|
|
202
202
|
## Database curation
|
|
203
203
|
|
|
204
|
-
The directory database is the product's moat. `scripts/regenerate-db.py` rebuilds it idempotently from the 11 source lists (annotated `[from <source>]`). `
|
|
204
|
+
The directory database is the product's moat. `scripts/regenerate-db.py` rebuilds it idempotently from the 11 source lists (annotated `[from <source>]`). `backlinkflow db:review` flags quality issues.
|
|
205
205
|
|
|
206
206
|
**Review verdict:**
|
|
207
207
|
- **1,123 entries**, 9 categories — full coverage from all 11 sources
|
|
@@ -216,22 +216,22 @@ The directory database is the product's moat. `scripts/regenerate-db.py` rebuild
|
|
|
216
216
|
```bash
|
|
217
217
|
python3 scripts/check-urls.py /tmp/linkflow-urls.txt # live-check all URLs
|
|
218
218
|
python3 scripts/apply-urlcheck.py # mark verified-dead in DB
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
backlinkflow db:review # see remaining flags
|
|
220
|
+
backlinkflow db:regenerate # rebuild from sources (idempotent)
|
|
221
|
+
backlinkflow stats # confirm counts
|
|
222
222
|
```
|
|
223
223
|
|
|
224
224
|
---
|
|
225
225
|
|
|
226
226
|
## The flow series
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
BacklinkFlow is the third in a series of zero-cost CLI tools, all MIT, all npm-published:
|
|
229
229
|
|
|
230
230
|
| Tool | Job | Engines | npm |
|
|
231
231
|
|---|---|---|---|
|
|
232
232
|
| **[SECflow](https://github.com/imsankz/SECflow)** | Security scanning for AI-driven repos | gitleaks, trivy, npm audit, custom regex | [`secflow`](https://www.npmjs.com/package/secflow) |
|
|
233
233
|
| **[SeoFlow](https://github.com/imsankz/seoflow)** | AI-powered SEO pipeline (audit, internal links, content gen, GSC) | GSC, PSI, Pexels, Ubersuggest, 6 LLMs | [`seoflow`](https://www.npmjs.com/package/seoflow) |
|
|
234
|
-
| **[
|
|
234
|
+
| **[BacklinkFlow](https://github.com/imsankz/backlinkflow)** | Backlink & directory submission automation | Your AI endpoint + Playwright | [`backlinkflow`](https://www.npmjs.com/package/backlinkflow) (this repo) |
|
|
235
235
|
|
|
236
236
|
Same DNA across all three: **free engines + your own AI agent as the "smart" layer + npm-published + zero-cost.**
|
|
237
237
|
|
|
@@ -240,7 +240,7 @@ Same DNA across all three: **free engines + your own AI agent as the "smart" lay
|
|
|
240
240
|
## Architecture
|
|
241
241
|
|
|
242
242
|
```
|
|
243
|
-
|
|
243
|
+
backlinkflow submit <url> --go
|
|
244
244
|
↓
|
|
245
245
|
config loader (linkflow.config.json + .env.local)
|
|
246
246
|
↓
|
|
@@ -317,4 +317,4 @@ MIT (see [LICENSE](./LICENSE)). Source data compiled from 11 open-source project
|
|
|
317
317
|
|
|
318
318
|
## ☕ Support
|
|
319
319
|
|
|
320
|
-
|
|
320
|
+
BacklinkFlow is free forever. If it saves you $79 (or 40 hours), [buy me a coffee](https://ko-fi.com/chasingwhereabouts) — it funds more free tools.
|
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;
|
|
@@ -53,11 +53,11 @@ Search "${q}": ${results.length} matches
|
|
|
53
53
|
async function cmdSubmit() {
|
|
54
54
|
const siteUrl = VERB_ARG;
|
|
55
55
|
if (!siteUrl) {
|
|
56
|
-
console.log("Usage:
|
|
56
|
+
console.log("Usage: backlinkflow submit <site-url> [--dry-run] [--limit N] [--category X] [--go]");
|
|
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)) {
|
|
@@ -132,7 +132,7 @@ async function cmdSubmit() {
|
|
|
132
132
|
async function cmdPayload() {
|
|
133
133
|
const siteUrl = VERB_ARG;
|
|
134
134
|
if (!siteUrl) {
|
|
135
|
-
console.log("Usage:
|
|
135
|
+
console.log("Usage: backlinkflow payload <site-url> [--directory X]");
|
|
136
136
|
process.exit(1);
|
|
137
137
|
}
|
|
138
138
|
const cfg = loadConfig(CONFIG_FILE);
|
|
@@ -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}`);
|
|
@@ -262,12 +262,12 @@ async function main() {
|
|
|
262
262
|
break;
|
|
263
263
|
default:
|
|
264
264
|
console.log(`Unknown command: ${VERB}
|
|
265
|
-
Run '
|
|
265
|
+
Run 'backlinkflow' with: list | search | submit | payload | status | report | stats | db:review | db:regenerate | init`);
|
|
266
266
|
process.exit(1);
|
|
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: linkflow 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: linkflow 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 'linkflow' 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/llms.txt
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
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",
|
|
7
7
|
"author": "Sankalp Singh",
|
|
8
8
|
"bin": {
|
|
9
|
-
"
|
|
9
|
+
"backlinkflow": "bin/linkflow.js"
|
|
10
10
|
},
|
|
11
11
|
"main": "dist/index.js",
|
|
12
12
|
"exports": {
|