myaidev-method 0.2.22 ā 0.2.24-1
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/USER_GUIDE.md +453 -48
- package/bin/cli.js +236 -38
- package/content-rules.example.md +80 -0
- package/dist/mcp/mcp-launcher.js +237 -0
- package/dist/server/.tsbuildinfo +1 -1
- package/dist/server/auth/layers.d.ts +1 -1
- package/dist/server/auth/services/AuthService.d.ts +1 -1
- package/dist/server/auth/services/TokenService.js.map +1 -1
- package/dist/server/auth/services/example.d.ts +5 -5
- package/package.json +22 -17
- package/src/config/workflows.js +28 -44
- package/src/index.js +21 -8
- package/src/lib/ascii-banner.js +214 -0
- package/src/lib/config-manager.js +470 -0
- package/src/lib/content-generator.js +427 -0
- package/src/lib/html-conversion-utils.js +843 -0
- package/src/lib/seo-optimizer.js +515 -0
- package/src/lib/update-manager.js +2 -1
- package/src/lib/visual-config-utils.js +321 -295
- package/src/lib/visual-generation-utils.js +1000 -811
- package/src/lib/wordpress-client.js +633 -0
- package/src/lib/workflow-installer.js +3 -3
- package/src/scripts/configure-wordpress-mcp.js +8 -3
- package/src/scripts/generate-visual-cli.js +365 -235
- package/src/scripts/html-conversion-cli.js +526 -0
- package/src/scripts/init/configure.js +436 -0
- package/src/scripts/init/install.js +460 -0
- package/src/scripts/ping.js +250 -0
- package/src/scripts/utils/file-utils.js +404 -0
- package/src/scripts/utils/logger.js +300 -0
- package/src/scripts/utils/write-content.js +293 -0
- package/src/scripts/wordpress/publish-to-wordpress.js +165 -0
- package/src/server/auth/services/TokenService.ts +1 -1
- package/src/templates/claude/agents/content-rules-setup.md +657 -0
- package/src/templates/claude/agents/content-writer.md +328 -1
- package/src/templates/claude/agents/visual-content-generator.md +311 -8
- package/src/templates/claude/commands/myai-configure.md +1 -1
- package/src/templates/claude/commands/myai-content-rules-setup.md +204 -0
- package/src/templates/claude/commands/myai-convert-html.md +186 -0
- package/src/templates/codex/commands/myai-content-rules-setup.md +85 -0
- package/src/templates/diagrams/architecture.d2 +52 -0
- package/src/templates/diagrams/flowchart.d2 +42 -0
- package/src/templates/diagrams/sequence.d2 +47 -0
- package/src/templates/docs/content-creation-guide.md +164 -0
- package/src/templates/docs/deployment-guide.md +336 -0
- package/src/templates/docs/visual-generation-guide.md +248 -0
- package/src/templates/docs/wordpress-publishing-guide.md +208 -0
- package/src/templates/gemini/commands/myai-content-rules-setup.toml +57 -0
- package/src/templates/infographics/comparison-table.html +347 -0
- package/src/templates/infographics/data-chart.html +268 -0
- package/src/templates/infographics/process-flow.html +365 -0
- package/.claude/mcp/sparc-orchestrator-server.js +0 -607
- package/.claude/mcp/wordpress-server.js +0 -1277
- package/src/agents/content-writer-prompt.md +0 -164
- package/src/agents/content-writer.json +0 -70
- package/src/templates/claude/mcp_config.json +0 -74
- package/src/templates/claude/slash_commands.json +0 -166
- package/src/templates/scripts/configure-wordpress-mcp.js +0 -181
- /package/src/scripts/{wordpress-health-check.js ā wordpress/wordpress-health-check.js} +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Write Content CLI
|
|
5
|
+
* Command-line wrapper for content generation
|
|
6
|
+
* Usage: node write-content.js "Topic" [options]
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ContentGenerator } from '../../lib/content-generator.js';
|
|
10
|
+
import { SEOOptimizer } from '../../lib/seo-optimizer.js';
|
|
11
|
+
import { logger, formatTable } from './logger.js';
|
|
12
|
+
import fs from 'fs-extra';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse command line arguments
|
|
17
|
+
* @param {string[]} args - Command line arguments
|
|
18
|
+
* @returns {Object} Parsed options
|
|
19
|
+
*/
|
|
20
|
+
function parseArgs(args) {
|
|
21
|
+
const options = {
|
|
22
|
+
topic: '',
|
|
23
|
+
contentType: 'blog-post',
|
|
24
|
+
wordCount: 1500,
|
|
25
|
+
tone: 'professional',
|
|
26
|
+
keywords: [],
|
|
27
|
+
outputDir: './content',
|
|
28
|
+
outputFile: '',
|
|
29
|
+
analyze: false,
|
|
30
|
+
help: false
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
let i = 0;
|
|
34
|
+
while (i < args.length) {
|
|
35
|
+
const arg = args[i];
|
|
36
|
+
|
|
37
|
+
if (arg === '--help' || arg === '-h') {
|
|
38
|
+
options.help = true;
|
|
39
|
+
} else if (arg === '--type' || arg === '-t') {
|
|
40
|
+
options.contentType = args[++i];
|
|
41
|
+
} else if (arg === '--word-count' || arg === '-w') {
|
|
42
|
+
options.wordCount = parseInt(args[++i], 10);
|
|
43
|
+
} else if (arg === '--tone') {
|
|
44
|
+
options.tone = args[++i];
|
|
45
|
+
} else if (arg === '--keywords' || arg === '-k') {
|
|
46
|
+
options.keywords = args[++i].split(',').map(k => k.trim());
|
|
47
|
+
} else if (arg === '--output' || arg === '-o') {
|
|
48
|
+
options.outputFile = args[++i];
|
|
49
|
+
} else if (arg === '--output-dir') {
|
|
50
|
+
options.outputDir = args[++i];
|
|
51
|
+
} else if (arg === '--analyze' || arg === '-a') {
|
|
52
|
+
options.analyze = true;
|
|
53
|
+
} else if (!arg.startsWith('-')) {
|
|
54
|
+
options.topic = arg;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
i++;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return options;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Show help message
|
|
65
|
+
*/
|
|
66
|
+
function showHelp() {
|
|
67
|
+
console.log(`
|
|
68
|
+
Write Content - Content Generation CLI
|
|
69
|
+
|
|
70
|
+
Usage: node write-content.js "Topic" [options]
|
|
71
|
+
|
|
72
|
+
Options:
|
|
73
|
+
-h, --help Show this help message
|
|
74
|
+
-t, --type <type> Content type (blog-post, tutorial, how-to, listicle, product-review, comparison)
|
|
75
|
+
-w, --word-count <num> Target word count (default: 1500)
|
|
76
|
+
--tone <tone> Writing tone (professional, conversational, technical, casual, authoritative)
|
|
77
|
+
-k, --keywords <list> Comma-separated keywords for SEO
|
|
78
|
+
-o, --output <file> Output filename (auto-generated if not specified)
|
|
79
|
+
--output-dir <dir> Output directory (default: ./content)
|
|
80
|
+
-a, --analyze Analyze content for SEO after generation
|
|
81
|
+
|
|
82
|
+
Examples:
|
|
83
|
+
node write-content.js "How to Train a Puppy" --type tutorial --tone conversational
|
|
84
|
+
node write-content.js "Best Laptops 2024" --type listicle --keywords "laptops,tech,reviews"
|
|
85
|
+
node write-content.js "React vs Vue" --type comparison --word-count 2500 --analyze
|
|
86
|
+
`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Generate content specification for the content-writer agent
|
|
91
|
+
* @param {Object} options - Generation options
|
|
92
|
+
*/
|
|
93
|
+
async function generateContentSpec(options) {
|
|
94
|
+
const generator = new ContentGenerator({
|
|
95
|
+
outputDir: options.outputDir,
|
|
96
|
+
tone: options.tone,
|
|
97
|
+
wordCount: options.wordCount
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await generator.initialize();
|
|
101
|
+
|
|
102
|
+
// Validate options
|
|
103
|
+
const validation = generator.validateOptions(options);
|
|
104
|
+
if (!validation.valid) {
|
|
105
|
+
for (const error of validation.errors) {
|
|
106
|
+
logger.error(error);
|
|
107
|
+
}
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Generate content specification
|
|
112
|
+
const spec = generator.generateContentSpec(options.topic, {
|
|
113
|
+
contentType: options.contentType,
|
|
114
|
+
wordCount: options.wordCount,
|
|
115
|
+
tone: options.tone,
|
|
116
|
+
keywords: options.keywords
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
logger.header('Content Specification');
|
|
120
|
+
logger.log(`Topic: ${spec.topic}`);
|
|
121
|
+
logger.log(`Type: ${spec.contentType}`);
|
|
122
|
+
logger.log(`Word Count Target: ${spec.wordCount}`);
|
|
123
|
+
logger.log(`Tone: ${spec.tone}`);
|
|
124
|
+
logger.log(`Keywords: ${spec.keywords.length > 0 ? spec.keywords.join(', ') : 'None specified'}`);
|
|
125
|
+
logger.blank();
|
|
126
|
+
|
|
127
|
+
logger.section('Required Sections');
|
|
128
|
+
for (const section of spec.sections) {
|
|
129
|
+
logger.log(` ⢠${section}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
logger.section('Frontmatter Fields');
|
|
133
|
+
for (const field of spec.requiredFrontmatter) {
|
|
134
|
+
logger.log(` ⢠${field}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Generate filename
|
|
138
|
+
const filename = options.outputFile || generator.generateFilename(options.topic);
|
|
139
|
+
const outputPath = path.join(options.outputDir, filename);
|
|
140
|
+
|
|
141
|
+
logger.section('Output');
|
|
142
|
+
logger.log(`Filename: ${filename}`);
|
|
143
|
+
logger.log(`Path: ${outputPath}`);
|
|
144
|
+
logger.blank();
|
|
145
|
+
|
|
146
|
+
// Generate frontmatter template
|
|
147
|
+
const frontmatterData = {
|
|
148
|
+
title: options.topic,
|
|
149
|
+
description: '',
|
|
150
|
+
date: new Date().toISOString().split('T')[0],
|
|
151
|
+
author: '',
|
|
152
|
+
categories: [],
|
|
153
|
+
tags: options.keywords,
|
|
154
|
+
status: 'draft'
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const frontmatter = generator.generateFrontmatter(frontmatterData);
|
|
158
|
+
|
|
159
|
+
// Create template file
|
|
160
|
+
const templateContent = `${frontmatter}
|
|
161
|
+
# ${options.topic}
|
|
162
|
+
|
|
163
|
+
<!-- Introduction -->
|
|
164
|
+
|
|
165
|
+
## Overview
|
|
166
|
+
|
|
167
|
+
[Write introduction here]
|
|
168
|
+
|
|
169
|
+
${spec.sections.slice(1).map(section => `
|
|
170
|
+
## ${section.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')}
|
|
171
|
+
|
|
172
|
+
[Write ${section} content here]
|
|
173
|
+
`).join('\n')}
|
|
174
|
+
|
|
175
|
+
<!-- Call to Action -->
|
|
176
|
+
|
|
177
|
+
## Conclusion
|
|
178
|
+
|
|
179
|
+
[Write conclusion here]
|
|
180
|
+
`;
|
|
181
|
+
|
|
182
|
+
await fs.ensureDir(options.outputDir);
|
|
183
|
+
await fs.writeFile(outputPath, templateContent, 'utf-8');
|
|
184
|
+
|
|
185
|
+
logger.success(`Template created: ${outputPath}`);
|
|
186
|
+
logger.blank();
|
|
187
|
+
logger.info('Use the content-writer agent to generate full content:');
|
|
188
|
+
logger.log(` /myai-content-writer "${options.topic}" --type ${options.contentType}`);
|
|
189
|
+
|
|
190
|
+
return { spec, outputPath, templateContent };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Analyze existing content file
|
|
195
|
+
* @param {string} filePath - Path to content file
|
|
196
|
+
* @param {string[]} keywords - Keywords to check
|
|
197
|
+
*/
|
|
198
|
+
async function analyzeContent(filePath, keywords) {
|
|
199
|
+
const optimizer = new SEOOptimizer();
|
|
200
|
+
|
|
201
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
202
|
+
|
|
203
|
+
// Extract title from frontmatter or first heading
|
|
204
|
+
const titleMatch = content.match(/^title:\s*(.+)$/m) || content.match(/^#\s+(.+)$/m);
|
|
205
|
+
const title = titleMatch ? titleMatch[1].trim() : '';
|
|
206
|
+
|
|
207
|
+
logger.header('SEO Analysis');
|
|
208
|
+
logger.log(`File: ${filePath}`);
|
|
209
|
+
logger.log(`Title: ${title || 'Not found'}`);
|
|
210
|
+
logger.blank();
|
|
211
|
+
|
|
212
|
+
const analysis = optimizer.analyzeSEO(content, keywords, title);
|
|
213
|
+
|
|
214
|
+
// Score display
|
|
215
|
+
const scoreColor = analysis.score >= 80 ? 'green' : analysis.score >= 60 ? 'yellow' : 'red';
|
|
216
|
+
logger.log(`SEO Score: ${analysis.score}/100 (Grade: ${analysis.grade})`);
|
|
217
|
+
logger.log(`Word Count: ${analysis.wordCount}`);
|
|
218
|
+
if (analysis.keywordDensity !== null) {
|
|
219
|
+
logger.log(`Keyword Density: ${analysis.keywordDensity}%`);
|
|
220
|
+
}
|
|
221
|
+
logger.blank();
|
|
222
|
+
|
|
223
|
+
// Issues
|
|
224
|
+
if (analysis.issues.length > 0) {
|
|
225
|
+
logger.section('Issues');
|
|
226
|
+
for (const issue of analysis.issues) {
|
|
227
|
+
logger.error(issue);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Recommendations
|
|
232
|
+
if (analysis.recommendations.length > 0) {
|
|
233
|
+
logger.section('Recommendations');
|
|
234
|
+
for (const rec of analysis.recommendations) {
|
|
235
|
+
logger.warn(rec);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Meta description
|
|
240
|
+
logger.section('Generated Meta Description');
|
|
241
|
+
const metaDesc = optimizer.generateMetaDescription(content);
|
|
242
|
+
logger.log(metaDesc);
|
|
243
|
+
logger.log(`Length: ${metaDesc.length} characters`);
|
|
244
|
+
|
|
245
|
+
// Slug
|
|
246
|
+
if (title) {
|
|
247
|
+
logger.section('Generated Slug');
|
|
248
|
+
logger.log(optimizer.generateSlug(title));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return analysis;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Main function
|
|
256
|
+
*/
|
|
257
|
+
async function main() {
|
|
258
|
+
const args = process.argv.slice(2);
|
|
259
|
+
const options = parseArgs(args);
|
|
260
|
+
|
|
261
|
+
if (options.help) {
|
|
262
|
+
showHelp();
|
|
263
|
+
process.exit(0);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (!options.topic && !options.analyze) {
|
|
267
|
+
logger.error('Topic is required. Use --help for usage information.');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
if (options.analyze && options.topic) {
|
|
273
|
+
// If topic looks like a file path, analyze it
|
|
274
|
+
if (options.topic.endsWith('.md') || options.topic.includes('/')) {
|
|
275
|
+
await analyzeContent(options.topic, options.keywords);
|
|
276
|
+
} else {
|
|
277
|
+
// Generate spec and analyze
|
|
278
|
+
const { outputPath } = await generateContentSpec(options);
|
|
279
|
+
await analyzeContent(outputPath, options.keywords);
|
|
280
|
+
}
|
|
281
|
+
} else if (options.analyze && !options.topic) {
|
|
282
|
+
logger.error('Please provide a file path to analyze.');
|
|
283
|
+
process.exit(1);
|
|
284
|
+
} else {
|
|
285
|
+
await generateContentSpec(options);
|
|
286
|
+
}
|
|
287
|
+
} catch (error) {
|
|
288
|
+
logger.error(`Error: ${error.message}`);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
main();
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Publish markdown content to WordPress
|
|
4
|
+
*
|
|
5
|
+
* Usage: node publish-to-wordpress.js <file-path> [--status draft|publish]
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from "fs";
|
|
9
|
+
import { resolve } from "path";
|
|
10
|
+
import matter from "gray-matter";
|
|
11
|
+
import dotenv from "dotenv";
|
|
12
|
+
import { marked } from "marked";
|
|
13
|
+
import { GutenbergConverter } from "../../mcp/gutenberg-converter.js";
|
|
14
|
+
import { WordPressMCP } from "../../mcp/wordpress-integration.js";
|
|
15
|
+
|
|
16
|
+
// Load environment variables
|
|
17
|
+
dotenv.config();
|
|
18
|
+
|
|
19
|
+
// Parse command line arguments
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
const filePath = args[0];
|
|
22
|
+
const statusIndex = args.indexOf("--status");
|
|
23
|
+
const requestedStatus =
|
|
24
|
+
statusIndex !== -1 && args[statusIndex + 1] ? args[statusIndex + 1] : "draft";
|
|
25
|
+
|
|
26
|
+
if (!filePath) {
|
|
27
|
+
console.error("Error: File path is required");
|
|
28
|
+
console.error(
|
|
29
|
+
"Usage: node publish-to-wordpress.js <file-path> [--status draft|publish]",
|
|
30
|
+
);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Validate WordPress configuration
|
|
35
|
+
if (
|
|
36
|
+
!process.env.WORDPRESS_URL ||
|
|
37
|
+
!process.env.WORDPRESS_USERNAME ||
|
|
38
|
+
!process.env.WORDPRESS_APP_PASSWORD
|
|
39
|
+
) {
|
|
40
|
+
console.error("Error: WordPress configuration is incomplete");
|
|
41
|
+
console.error("Required environment variables:");
|
|
42
|
+
console.error(" - WORDPRESS_URL");
|
|
43
|
+
console.error(" - WORDPRESS_USERNAME");
|
|
44
|
+
console.error(" - WORDPRESS_APP_PASSWORD");
|
|
45
|
+
console.error(
|
|
46
|
+
"\nRun /myai-configure wordpress to set up your WordPress connection",
|
|
47
|
+
);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function publishToWordPress() {
|
|
52
|
+
try {
|
|
53
|
+
// Read and parse markdown file
|
|
54
|
+
const absolutePath = resolve(filePath);
|
|
55
|
+
const fileContent = readFileSync(absolutePath, "utf-8");
|
|
56
|
+
const { data: frontmatter, content } = matter(fileContent);
|
|
57
|
+
|
|
58
|
+
console.log("š Processing file:", filePath);
|
|
59
|
+
console.log("š Title:", frontmatter.title || "No title");
|
|
60
|
+
|
|
61
|
+
// Extract interactive blocks before markdown conversion
|
|
62
|
+
// const { markdown: cleanMarkdown, interactiveBlocks } =
|
|
63
|
+
// GutenbergConverter.extractInteractiveBlocks(content);
|
|
64
|
+
|
|
65
|
+
// if (interactiveBlocks.length > 0) {
|
|
66
|
+
// console.log(
|
|
67
|
+
// ` Interactive blocks: ${interactiveBlocks.length} found`,
|
|
68
|
+
// );
|
|
69
|
+
// }
|
|
70
|
+
|
|
71
|
+
// Convert markdown to HTML using marked (preserves placeholders)
|
|
72
|
+
const html = marked.parse(content);
|
|
73
|
+
|
|
74
|
+
// Convert HTML to Gutenberg blocks
|
|
75
|
+
let htmlContent = GutenbergConverter.htmlToGutenberg(html);
|
|
76
|
+
|
|
77
|
+
// Restore interactive blocks as raw HTML blocks
|
|
78
|
+
// if (interactiveBlocks.length > 0) {
|
|
79
|
+
// htmlContent = GutenbergConverter.restoreInteractiveBlocks(
|
|
80
|
+
// htmlContent,
|
|
81
|
+
// interactiveBlocks,
|
|
82
|
+
// );
|
|
83
|
+
// }
|
|
84
|
+
|
|
85
|
+
// Prepare WordPress configuration
|
|
86
|
+
const config = {
|
|
87
|
+
endpoint: {
|
|
88
|
+
url: process.env.WORDPRESS_URL,
|
|
89
|
+
api_version: "wp/v2",
|
|
90
|
+
authentication: {
|
|
91
|
+
username: process.env.WORDPRESS_USERNAME,
|
|
92
|
+
password: process.env.WORDPRESS_APP_PASSWORD,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
defaults: {
|
|
96
|
+
post_status: requestedStatus,
|
|
97
|
+
format: "standard",
|
|
98
|
+
comment_status: "open",
|
|
99
|
+
ping_status: "open",
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Initialize WordPress client
|
|
104
|
+
const wp = new WordPressMCP(config);
|
|
105
|
+
|
|
106
|
+
// Prepare post data
|
|
107
|
+
const postParams = {
|
|
108
|
+
title: frontmatter.title || "Untitled",
|
|
109
|
+
content: htmlContent,
|
|
110
|
+
status: requestedStatus,
|
|
111
|
+
excerpt: frontmatter.meta_description || "",
|
|
112
|
+
slug: frontmatter.slug || "",
|
|
113
|
+
use_gutenberg: process.env.USE_GUTENBERG === "true",
|
|
114
|
+
// Note: Tags and categories require ID mapping which will be added in future versions
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
console.log("š Publishing to WordPress...");
|
|
118
|
+
console.log(" URL:", process.env.WORDPRESS_URL);
|
|
119
|
+
console.log(" Status:", requestedStatus);
|
|
120
|
+
console.log(
|
|
121
|
+
" Format:",
|
|
122
|
+
postParams.use_gutenberg ? "Gutenberg" : "Classic",
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Create the post
|
|
126
|
+
const result = await wp.createPost(postParams);
|
|
127
|
+
|
|
128
|
+
// Success!
|
|
129
|
+
console.log("\nā
Successfully published to WordPress!");
|
|
130
|
+
console.log("\nš Post Details:");
|
|
131
|
+
console.log(" Post ID:", result.id);
|
|
132
|
+
console.log(" Title:", result.title.rendered);
|
|
133
|
+
console.log(" Status:", result.status);
|
|
134
|
+
console.log(" Slug:", result.slug);
|
|
135
|
+
|
|
136
|
+
console.log("\nš URLs:");
|
|
137
|
+
console.log(" View:", result.link);
|
|
138
|
+
console.log(
|
|
139
|
+
" Edit:",
|
|
140
|
+
`${process.env.WORDPRESS_URL}/wp-admin/post.php?post=${result.id}&action=edit`,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return result;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error("\nā Error publishing to WordPress:");
|
|
146
|
+
console.error(error.message);
|
|
147
|
+
|
|
148
|
+
if (error.message.includes("401")) {
|
|
149
|
+
console.error("\nš Authentication failed. Please check:");
|
|
150
|
+
console.error(" 1. Your WordPress username is correct");
|
|
151
|
+
console.error(" 2. Your Application Password is valid");
|
|
152
|
+
console.error(" 3. Application Passwords are enabled on your site");
|
|
153
|
+
} else if (error.message.includes("404")) {
|
|
154
|
+
console.error("\nš WordPress site not found. Please check:");
|
|
155
|
+
console.error(" 1. Your WORDPRESS_URL is correct");
|
|
156
|
+
console.error(" 2. The site is accessible");
|
|
157
|
+
console.error(" 3. The REST API is enabled");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Run the publisher
|
|
165
|
+
publishToWordPress();
|
|
@@ -6,7 +6,7 @@ import { AuthError, JWTPayload } from "../../../shared/types.js";
|
|
|
6
6
|
const TOKEN_EXPIRY_DAYS = 7;
|
|
7
7
|
const ALGORITHM = "RS256";
|
|
8
8
|
|
|
9
|
-
let keyPair: { publicKey: jose.
|
|
9
|
+
let keyPair: { publicKey: jose.CryptoKey; privateKey: jose.CryptoKey } | null = null;
|
|
10
10
|
|
|
11
11
|
export class TokenService extends Context.Tag("TokenService")<
|
|
12
12
|
TokenService,
|