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 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
- ### Options
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
- ### How it works
36
+ #### How push works
36
37
 
37
- 1. Open your Fogtrail dashboard and click **Add Content** > **Import from Website**
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
- ### Example
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
@@ -0,0 +1,7 @@
1
+ interface PullOptions {
2
+ token: string;
3
+ dir: string;
4
+ url: string;
5
+ }
6
+ export declare function pullCommand(options: PullOptions): Promise<void>;
7
+ export {};
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fogtrail-cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "CLI tool to push blog content to Fogtrail for AEO optimization",
5
5
  "bin": {
6
6
  "fogtrail-cli": "./dist/index.js"