fogtrail-cli 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -5
- package/dist/index.js +9 -1
- package/dist/pull.d.ts +7 -0
- package/dist/pull.js +117 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# fogtrail-cli
|
|
2
2
|
|
|
3
|
-
CLI tool to push blog content to your [Fogtrail](https://fogtrail.io) instance for AEO (Answer Engine Optimization).
|
|
3
|
+
CLI tool to push and pull blog content to/from your [Fogtrail](https://fogtrail.io) instance for AEO (Answer Engine Optimization).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Use directly with npx (no install needed)
|
|
9
9
|
npx fogtrail-cli push --token <TOKEN> --dir ./blogs
|
|
10
|
+
npx fogtrail-cli pull --token <TOKEN> --dir ./output
|
|
10
11
|
|
|
11
12
|
# Or install globally
|
|
12
13
|
npm install -g fogtrail-cli
|
|
@@ -22,7 +23,7 @@ Push all markdown and HTML files from a directory to Fogtrail:
|
|
|
22
23
|
fogtrail-cli push --token <TOKEN> --dir ./blogs --url https://your-fogtrail-instance.com
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
#### Push options
|
|
26
27
|
|
|
27
28
|
| Option | Required | Default | Description |
|
|
28
29
|
|--------|----------|---------|-------------|
|
|
@@ -32,21 +33,45 @@ fogtrail-cli push --token <TOKEN> --dir ./blogs --url https://your-fogtrail-inst
|
|
|
32
33
|
| `--batch-size <size>` | No | `10` | Number of files per upload batch |
|
|
33
34
|
| `--no-recursive` | No | - | Do not scan subdirectories |
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
#### How push works
|
|
36
37
|
|
|
37
|
-
1. Open your Fogtrail dashboard and click **Add Content** > **Import
|
|
38
|
+
1. Open your Fogtrail dashboard and click **Add Content** > **Import via CLI**
|
|
38
39
|
2. Copy the command shown in the dialog (includes your unique token)
|
|
39
40
|
3. Run the command in your terminal from your project directory
|
|
40
41
|
4. The CLI scans for `.md` and `.html` files, uploads them to Fogtrail
|
|
41
42
|
5. HTML files are automatically converted to Markdown
|
|
42
43
|
6. Files without YAML frontmatter get auto-generated metadata
|
|
43
44
|
|
|
45
|
+
### Pull content
|
|
46
|
+
|
|
47
|
+
Pull all content from Fogtrail to local markdown files:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
fogtrail-cli pull --token <TOKEN> --dir ./output --url https://your-fogtrail-instance.com
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
#### Pull options
|
|
54
|
+
|
|
55
|
+
| Option | Required | Default | Description |
|
|
56
|
+
|--------|----------|---------|-------------|
|
|
57
|
+
| `--token <token>` | Yes | - | Export session token from Fogtrail UI |
|
|
58
|
+
| `--dir <directory>` | Yes | - | Directory to write exported files |
|
|
59
|
+
| `--url <url>` | No | `http://localhost:3000` | Fogtrail instance URL |
|
|
60
|
+
|
|
61
|
+
#### How pull works
|
|
62
|
+
|
|
63
|
+
1. Open your Fogtrail dashboard and click **Publish** > **Export via CLI**
|
|
64
|
+
2. Copy the command shown in the dialog (includes your unique token)
|
|
65
|
+
3. Run the command in your terminal
|
|
66
|
+
4. All content is downloaded as Markdown files with YAML frontmatter
|
|
67
|
+
5. The token is single-use and expires after 15 minutes
|
|
68
|
+
|
|
44
69
|
### Supported file formats
|
|
45
70
|
|
|
46
71
|
- **Markdown** (`.md`) - uploaded as-is with frontmatter validation
|
|
47
72
|
- **HTML** (`.html`, `.htm`) - converted to Markdown before upload
|
|
48
73
|
|
|
49
|
-
###
|
|
74
|
+
### Examples
|
|
50
75
|
|
|
51
76
|
```bash
|
|
52
77
|
# Push all blog posts from ./content/blog
|
|
@@ -54,6 +79,12 @@ npx fogtrail-cli push \
|
|
|
54
79
|
--token abc123def456... \
|
|
55
80
|
--dir ./content/blog \
|
|
56
81
|
--url https://app.fogtrail.io
|
|
82
|
+
|
|
83
|
+
# Pull all content to ./exported
|
|
84
|
+
npx fogtrail-cli pull \
|
|
85
|
+
--token fed654cba321... \
|
|
86
|
+
--dir ./exported \
|
|
87
|
+
--url https://app.fogtrail.io
|
|
57
88
|
```
|
|
58
89
|
|
|
59
90
|
## License
|
package/dist/index.js
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const commander_1 = require("commander");
|
|
5
5
|
const push_1 = require("./push");
|
|
6
|
+
const pull_1 = require("./pull");
|
|
6
7
|
const program = new commander_1.Command();
|
|
7
8
|
program
|
|
8
9
|
.name('fogtrail-cli')
|
|
9
|
-
.description('Push blog content to your Fogtrail instance')
|
|
10
|
+
.description('Push and pull blog content to/from your Fogtrail instance')
|
|
10
11
|
.version('0.1.0');
|
|
11
12
|
program
|
|
12
13
|
.command('push')
|
|
@@ -17,4 +18,11 @@ program
|
|
|
17
18
|
.option('--batch-size <size>', 'Files per request batch', '10')
|
|
18
19
|
.option('--no-recursive', 'Do not scan subdirectories')
|
|
19
20
|
.action(push_1.pushCommand);
|
|
21
|
+
program
|
|
22
|
+
.command('pull')
|
|
23
|
+
.description('Pull content from Fogtrail to local markdown files')
|
|
24
|
+
.requiredOption('--token <token>', 'Export session token (from Fogtrail UI)')
|
|
25
|
+
.requiredOption('--dir <directory>', 'Directory to write exported files')
|
|
26
|
+
.option('--url <url>', 'Fogtrail instance URL', 'http://localhost:3000')
|
|
27
|
+
.action(pull_1.pullCommand);
|
|
20
28
|
program.parse();
|
package/dist/pull.d.ts
ADDED
package/dist/pull.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.pullCommand = pullCommand;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
async function pullCommand(options) {
|
|
40
|
+
const { token, dir, url } = options;
|
|
41
|
+
// Ensure output directory exists
|
|
42
|
+
const resolvedDir = path.resolve(dir);
|
|
43
|
+
if (!fs.existsSync(resolvedDir)) {
|
|
44
|
+
fs.mkdirSync(resolvedDir, { recursive: true });
|
|
45
|
+
console.log(`Created directory: ${resolvedDir}`);
|
|
46
|
+
}
|
|
47
|
+
else if (!fs.statSync(resolvedDir).isDirectory()) {
|
|
48
|
+
console.error(`Error: Not a directory: ${resolvedDir}`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
const apiUrl = `${url.replace(/\/$/, '')}/api/export/pull`;
|
|
52
|
+
console.log('Pulling content from Fogtrail...');
|
|
53
|
+
try {
|
|
54
|
+
const response = await fetch(apiUrl, {
|
|
55
|
+
method: 'GET',
|
|
56
|
+
headers: {
|
|
57
|
+
'Authorization': `Bearer ${token}`,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
const data = await response.json().catch(() => ({ error: `HTTP ${response.status}` }));
|
|
62
|
+
if (response.status === 401) {
|
|
63
|
+
console.error(`Error: Invalid token. Please check your token and try again.`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
if (response.status === 410) {
|
|
67
|
+
console.error(`Error: Session expired or already used. Please create a new export session from the Fogtrail UI.`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
console.error(`Error: ${data.error || `HTTP ${response.status}`}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const data = await response.json();
|
|
74
|
+
if (!data.success || !data.files) {
|
|
75
|
+
console.error(`Error: ${data.error || 'Failed to pull content'}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
if (data.files.length === 0) {
|
|
79
|
+
console.log('No content to export.');
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
console.log(`Received ${data.files.length} file${data.files.length !== 1 ? 's' : ''}`);
|
|
83
|
+
console.log();
|
|
84
|
+
// Write files to disk
|
|
85
|
+
let written = 0;
|
|
86
|
+
let writeErrors = 0;
|
|
87
|
+
for (const file of data.files) {
|
|
88
|
+
const filepath = path.join(resolvedDir, file.filename);
|
|
89
|
+
try {
|
|
90
|
+
fs.writeFileSync(filepath, file.content, 'utf-8');
|
|
91
|
+
console.log(` ${file.filename}`);
|
|
92
|
+
written++;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
const err = error;
|
|
96
|
+
console.error(` Warning: Failed to write ${file.filename}: ${err.message}`);
|
|
97
|
+
writeErrors++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Summary
|
|
101
|
+
console.log();
|
|
102
|
+
console.log(`Successfully exported ${written} file${written !== 1 ? 's' : ''} to ${resolvedDir}`);
|
|
103
|
+
if (writeErrors > 0) {
|
|
104
|
+
console.log(`${writeErrors} file${writeErrors !== 1 ? 's' : ''} failed to write`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
const err = error;
|
|
109
|
+
if (err.message.includes('ECONNREFUSED') || err.message.includes('fetch failed')) {
|
|
110
|
+
console.error(`Error: Could not connect to ${url}. Make sure your Fogtrail instance is running.`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.error(`Error: ${err.message}`);
|
|
114
|
+
}
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|