backend-manager 5.9.0 → 5.9.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/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
14
14
|
- `Fixed` for any bug fixes.
|
|
15
15
|
- `Security` in case of vulnerabilities.
|
|
16
16
|
|
|
17
|
+
# [5.9.2] - 2026-06-20
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- Blog generation extended test (`test/content/blog-generate.js`). Run `npx mgr test mgr:content/blog-generate --extended` to exercise the full blog auto-publisher pipeline (source resolution → Ghostii → publish). Supports `BLOG_SOURCE`, `BLOG_NO_PUBLISH`, `BLOG_OPEN` env vars.
|
|
21
|
+
|
|
22
|
+
# [5.9.1] - 2026-06-20
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Missing `require('node-powertools')` in newsletter generator — broke `powertools.arrayify()` for array content support added in v5.9.0
|
|
26
|
+
|
|
17
27
|
# [5.9.0] - 2026-06-19
|
|
18
28
|
|
|
19
29
|
### BREAKING
|
package/package.json
CHANGED
|
@@ -260,6 +260,7 @@
|
|
|
260
260
|
"675hosting.com",
|
|
261
261
|
"675hosting.net",
|
|
262
262
|
"675hosting.org",
|
|
263
|
+
"67899vip.com",
|
|
263
264
|
"681mail.com",
|
|
264
265
|
"689345.xyz",
|
|
265
266
|
"6974.click",
|
|
@@ -879,6 +880,8 @@
|
|
|
879
880
|
"badopsec.lol",
|
|
880
881
|
"badpotato.tk",
|
|
881
882
|
"bahlill.cfd",
|
|
883
|
+
"baicai1145.online",
|
|
884
|
+
"baicai1145.shop",
|
|
882
885
|
"baileybridge.org",
|
|
883
886
|
"baiyucraft.sbs",
|
|
884
887
|
"baizoto.shop",
|
|
@@ -1200,6 +1203,7 @@
|
|
|
1200
1203
|
"cantozil.com",
|
|
1201
1204
|
"canvect.com",
|
|
1202
1205
|
"caowo.online",
|
|
1206
|
+
"capct.id",
|
|
1203
1207
|
"capcud.shop",
|
|
1204
1208
|
"capcut.digital",
|
|
1205
1209
|
"capcutku.io",
|
|
@@ -1667,6 +1671,7 @@
|
|
|
1667
1671
|
"devsonline.studio",
|
|
1668
1672
|
"devthien.io.vn",
|
|
1669
1673
|
"deyom.com",
|
|
1674
|
+
"dfghjertyh.dynv6.net",
|
|
1670
1675
|
"dfyuih.eu.cc",
|
|
1671
1676
|
"dghdfjfd.eu.cc",
|
|
1672
1677
|
"dh372.top",
|
|
@@ -2656,6 +2661,7 @@
|
|
|
2656
2661
|
"gghjnvgfyjhk.top",
|
|
2657
2662
|
"ggmal.ml",
|
|
2658
2663
|
"ggrainn.com",
|
|
2664
|
+
"ggvendas.store",
|
|
2659
2665
|
"ggvk.ru",
|
|
2660
2666
|
"ggvk.store",
|
|
2661
2667
|
"ggxhunter.com",
|
|
@@ -3150,6 +3156,7 @@
|
|
|
3150
3156
|
"http.nyc.mn",
|
|
3151
3157
|
"httpsgreenwichmeantime.in",
|
|
3152
3158
|
"huage.me",
|
|
3159
|
+
"huangao.xyz",
|
|
3153
3160
|
"huangniu8.com",
|
|
3154
3161
|
"hubglee.com",
|
|
3155
3162
|
"hubhost.store",
|
|
@@ -3512,6 +3519,7 @@
|
|
|
3512
3519
|
"joseihorumon.info",
|
|
3513
3520
|
"josse.ltd",
|
|
3514
3521
|
"jourrapide.com",
|
|
3522
|
+
"joywavepoint.com",
|
|
3515
3523
|
"jp-ml.com",
|
|
3516
3524
|
"jpco.org",
|
|
3517
3525
|
"jrhmail.com",
|
|
@@ -3605,6 +3613,7 @@
|
|
|
3605
3613
|
"kenfern.com",
|
|
3606
3614
|
"kenhbanme.com",
|
|
3607
3615
|
"kenhphim.net",
|
|
3616
|
+
"kenvanharen.com",
|
|
3608
3617
|
"kerotu.com",
|
|
3609
3618
|
"ket-qua.org",
|
|
3610
3619
|
"ketua.id",
|
|
@@ -4111,6 +4120,7 @@
|
|
|
4111
4120
|
"mailjunk.gq",
|
|
4112
4121
|
"mailjunk.ml",
|
|
4113
4122
|
"mailjunk.tk",
|
|
4123
|
+
"mailku.co",
|
|
4114
4124
|
"mailllshop.website",
|
|
4115
4125
|
"maillog.uk",
|
|
4116
4126
|
"mailmagnet.co",
|
|
@@ -4192,6 +4202,7 @@
|
|
|
4192
4202
|
"mailtv.net",
|
|
4193
4203
|
"mailtv.tv",
|
|
4194
4204
|
"mailuniverse.co.uk",
|
|
4205
|
+
"mailvip.net",
|
|
4195
4206
|
"mailw.site",
|
|
4196
4207
|
"mailyan.my.id",
|
|
4197
4208
|
"mailzi.ru",
|
|
@@ -4570,6 +4581,7 @@
|
|
|
4570
4581
|
"muathegame.com",
|
|
4571
4582
|
"muchomail.com",
|
|
4572
4583
|
"mucincanon.com",
|
|
4584
|
+
"mudahmaxwin.com",
|
|
4573
4585
|
"muehlacker.tk",
|
|
4574
4586
|
"muell.icu",
|
|
4575
4587
|
"muell.io",
|
|
@@ -4686,6 +4698,7 @@
|
|
|
4686
4698
|
"narsub.shop",
|
|
4687
4699
|
"nashapoultryshop.com",
|
|
4688
4700
|
"naslazhdai.ru",
|
|
4701
|
+
"natachai.me",
|
|
4689
4702
|
"natange.org.uk",
|
|
4690
4703
|
"nationalgardeningclub.com",
|
|
4691
4704
|
"natuanal.com",
|
|
@@ -4860,6 +4873,7 @@
|
|
|
4860
4873
|
"notrnailinator.com",
|
|
4861
4874
|
"notsharingmy.info",
|
|
4862
4875
|
"novaemail.com",
|
|
4876
|
+
"novaprime.vip",
|
|
4863
4877
|
"novasub.click",
|
|
4864
4878
|
"novyrix.cfd",
|
|
4865
4879
|
"now.im",
|
|
@@ -4896,6 +4910,7 @@
|
|
|
4896
4910
|
"nwrlr.org",
|
|
4897
4911
|
"nwytg.com",
|
|
4898
4912
|
"nwytg.net",
|
|
4913
|
+
"nxdbxd.top",
|
|
4899
4914
|
"ny7.me",
|
|
4900
4915
|
"nyasan.com",
|
|
4901
4916
|
"nyfhk.com",
|
|
@@ -5599,6 +5614,7 @@
|
|
|
5599
5614
|
"rvneous.com",
|
|
5600
5615
|
"rwstatus.com",
|
|
5601
5616
|
"rwxsxnomo.top",
|
|
5617
|
+
"rxinai.com",
|
|
5602
5618
|
"rxs9.cn",
|
|
5603
5619
|
"rygel.infos.st",
|
|
5604
5620
|
"ryteto.me",
|
|
@@ -5865,6 +5881,7 @@
|
|
|
5865
5881
|
"smarttalent.pw",
|
|
5866
5882
|
"smarttickethub.com",
|
|
5867
5883
|
"smashmail.de",
|
|
5884
|
+
"smell.sbs",
|
|
5868
5885
|
"smellfear.com",
|
|
5869
5886
|
"smellrear.com",
|
|
5870
5887
|
"smellypotato.tk",
|
|
@@ -7351,6 +7368,17 @@
|
|
|
7351
7368
|
"yours.tools",
|
|
7352
7369
|
"yourspamgoesto.space",
|
|
7353
7370
|
"yourtube.ml",
|
|
7371
|
+
"youtube-com-watch-jtpdc8khnpi.accesscam.org",
|
|
7372
|
+
"youtube-com-watch-jtpdc8khnpi.bond",
|
|
7373
|
+
"youtube-com-watch-jtpdc8khnpi.cyou",
|
|
7374
|
+
"youtube-com-watch-jtpdc8khnpi.dedyn.io",
|
|
7375
|
+
"youtube-com-watch-jtpdc8khnpi.freeddns.org",
|
|
7376
|
+
"youtube-com-watch-jtpdc8khnpi.gleeze.com",
|
|
7377
|
+
"youtube-com-watch-jtpdc8khnpi.loseyourip.com",
|
|
7378
|
+
"youtube-com-watch-jtpdc8khnpi.mywire.org",
|
|
7379
|
+
"youtube-com-watch-jtpdc8khnpi.ooguy.com",
|
|
7380
|
+
"youtube-com-watch-jtpdc8khnpi.theworkpc.com",
|
|
7381
|
+
"youtube-com-watch-jtpdc8khnpi.webredirect.org",
|
|
7354
7382
|
"youxiang.dev",
|
|
7355
7383
|
"yroid.com",
|
|
7356
7384
|
"yrppwsekv.top",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* the markdown pipeline (content) if needed.
|
|
22
22
|
*/
|
|
23
23
|
const fetch = require('wonderful-fetch');
|
|
24
|
+
const powertools = require('node-powertools');
|
|
24
25
|
|
|
25
26
|
const { filterSources } = require('./lib/filter.js');
|
|
26
27
|
const { generateStructure } = require('./lib/structure.js');
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blog article generation test.
|
|
3
|
+
*
|
|
4
|
+
* Two modes:
|
|
5
|
+
*
|
|
6
|
+
* 1. DEFAULT MODE (no env required)
|
|
7
|
+
* Verifies the blog auto-publisher config is readable and source resolution
|
|
8
|
+
* works correctly. No AI calls, no publishing. Fast, free, deterministic.
|
|
9
|
+
*
|
|
10
|
+
* 2. AI PIPELINE MODE (set TEST_EXTENDED_MODE=1)
|
|
11
|
+
* Runs the full blog auto-publisher pipeline: resolves a source, calls
|
|
12
|
+
* Ghostii to write an article, and publishes it to the website repo via
|
|
13
|
+
* admin/post. Same code path the daily cron uses. Costs money (AI tokens).
|
|
14
|
+
*
|
|
15
|
+
* Run from any consumer project's functions/ directory:
|
|
16
|
+
* npx mgr test mgr:content/blog-generate # config check (fast, free)
|
|
17
|
+
* TEST_EXTENDED_MODE=1 npx mgr test mgr:content/blog-generate # full AI pipeline
|
|
18
|
+
*
|
|
19
|
+
* --- Env vars (AI mode only, require TEST_EXTENDED_MODE=1) ---
|
|
20
|
+
* BLOG_SOURCE=<type> Override the source type for this run.
|
|
21
|
+
* Values: '$brand', '$parent', '$feed:<url>', a URL, or text.
|
|
22
|
+
* Default: picks randomly from the first content entry's sources[].
|
|
23
|
+
* BLOG_NO_PUBLISH=1 Generate the article but do NOT publish to the website repo.
|
|
24
|
+
* Useful for iterating on prompt quality without committing posts.
|
|
25
|
+
* BLOG_OPEN=1 Auto-open the generated article in the default browser (macOS only).
|
|
26
|
+
*
|
|
27
|
+
* AI mode requires:
|
|
28
|
+
* BACKEND_MANAGER_KEY — authenticates with Ghostii + parent server
|
|
29
|
+
* OPENAI_API_KEY — Ghostii uses OpenAI internally (or BACKEND_MANAGER_OPENAI_API_KEY)
|
|
30
|
+
* PARENT_API_URL — or set `parent` in backend-manager-config.json (for $parent sources)
|
|
31
|
+
*
|
|
32
|
+
* Default mode requires: nothing.
|
|
33
|
+
*/
|
|
34
|
+
const path = require('path');
|
|
35
|
+
const { execSync } = require('child_process');
|
|
36
|
+
const jetpack = require('fs-jetpack');
|
|
37
|
+
const powertools = require('node-powertools');
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
description: 'Generate a blog article (config check by default, full AI pipeline with TEST_EXTENDED_MODE)',
|
|
41
|
+
auth: 'none',
|
|
42
|
+
timeout: 300000,
|
|
43
|
+
async run({ assert, config, Manager, assistant, skip }) {
|
|
44
|
+
const env = process.env;
|
|
45
|
+
const blogConfig = Manager.config.blog;
|
|
46
|
+
|
|
47
|
+
// --- Default mode: config validation ---
|
|
48
|
+
if (!env.TEST_EXTENDED_MODE) {
|
|
49
|
+
assert.ok(blogConfig, 'blog config exists');
|
|
50
|
+
assert.ok(blogConfig.platform, 'blog.platform is set');
|
|
51
|
+
assert.equal(typeof blogConfig.enabled, 'boolean', 'blog.enabled is a boolean');
|
|
52
|
+
|
|
53
|
+
const contentArray = powertools.arrayify(blogConfig.content);
|
|
54
|
+
assert.ok(contentArray.length > 0, 'blog.content has at least one entry');
|
|
55
|
+
|
|
56
|
+
const entry = contentArray[0];
|
|
57
|
+
assert.ok(Array.isArray(entry.sources), 'first entry has sources array');
|
|
58
|
+
assert.ok(entry.sources.length > 0, 'first entry has at least one source');
|
|
59
|
+
|
|
60
|
+
// Verify source types are valid
|
|
61
|
+
for (const source of entry.sources) {
|
|
62
|
+
const validTypes = source === '$brand'
|
|
63
|
+
|| source === '$parent'
|
|
64
|
+
|| source.startsWith('$feed:')
|
|
65
|
+
|| source.startsWith('http')
|
|
66
|
+
|| typeof source === 'string';
|
|
67
|
+
assert.ok(validTypes, `source "${source}" is a valid type`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Verify provider exists
|
|
71
|
+
const providerPath = path.join(__dirname, '..', '..', 'src', 'manager', 'libraries', 'content', `${blogConfig.platform}.js`);
|
|
72
|
+
assert.ok(jetpack.exists(providerPath), `provider "${blogConfig.platform}" exists at ${providerPath}`);
|
|
73
|
+
|
|
74
|
+
console.log(`\n[blog-generate] Config OK:`);
|
|
75
|
+
console.log(` platform: ${blogConfig.platform}`);
|
|
76
|
+
console.log(` enabled: ${blogConfig.enabled}`);
|
|
77
|
+
console.log(` entries: ${contentArray.length}`);
|
|
78
|
+
console.log(` sources: ${entry.sources.join(', ')}`);
|
|
79
|
+
console.log(` categories: ${(entry.categories || []).join(', ') || '(none)'}`);
|
|
80
|
+
console.log(` tone: ${entry.tone || '(default)'}`);
|
|
81
|
+
console.log(` (Set TEST_EXTENDED_MODE=1 to run the full AI pipeline.)`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// --- Extended mode: full AI pipeline ---
|
|
86
|
+
if (!blogConfig?.enabled) {
|
|
87
|
+
return skip('blog.enabled is false in config');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const publisherPath = path.join(__dirname, '..', '..', 'src', 'manager', 'events', 'cron', 'daily', 'blog-auto-publisher.js');
|
|
91
|
+
const publisher = require(publisherPath);
|
|
92
|
+
|
|
93
|
+
// Get content entries
|
|
94
|
+
const contentArray = powertools.arrayify(blogConfig.content);
|
|
95
|
+
assert.ok(contentArray.length > 0, 'blog.content has at least one entry');
|
|
96
|
+
|
|
97
|
+
// Clone and override for test
|
|
98
|
+
const content = JSON.parse(JSON.stringify(contentArray));
|
|
99
|
+
content[0].chance = 1.0;
|
|
100
|
+
content[0].quantity = 1;
|
|
101
|
+
|
|
102
|
+
// Override source if env specified
|
|
103
|
+
if (env.BLOG_SOURCE) {
|
|
104
|
+
content[0].sources = [env.BLOG_SOURCE];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
Manager.config.blog.content = content;
|
|
108
|
+
|
|
109
|
+
// Output dir
|
|
110
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-').replace(/-(\d{3})Z$/, '');
|
|
111
|
+
const outDir = path.join(process.cwd(), '..', '.temp', 'blog', `run-${stamp}`);
|
|
112
|
+
jetpack.dir(outDir);
|
|
113
|
+
|
|
114
|
+
console.log(`\n[blog-generate] Extended mode — full AI pipeline`);
|
|
115
|
+
console.log(` platform: ${blogConfig.platform}`);
|
|
116
|
+
console.log(` sources: ${content[0].sources.join(', ')}`);
|
|
117
|
+
console.log(` publish: ${env.BLOG_NO_PUBLISH ? 'NO (BLOG_NO_PUBLISH=1)' : 'YES'}`);
|
|
118
|
+
console.log(` output: ${outDir}`);
|
|
119
|
+
|
|
120
|
+
// Intercept publishArticle if BLOG_NO_PUBLISH is set
|
|
121
|
+
if (env.BLOG_NO_PUBLISH) {
|
|
122
|
+
const provider = require(path.join(__dirname, '..', '..', 'src', 'manager', 'libraries', 'content', `${blogConfig.platform}.js`));
|
|
123
|
+
const originalPublish = provider.publishArticle;
|
|
124
|
+
provider.publishArticle = async (ast, args) => {
|
|
125
|
+
console.log(`[blog-generate] SKIPPED publishArticle (BLOG_NO_PUBLISH=1)`);
|
|
126
|
+
console.log(` title: ${args.article?.title || '(unknown)'}`);
|
|
127
|
+
return { post: null, url: null, slug: 'dry-run', path: null };
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Restore after test
|
|
131
|
+
process.on('exit', () => { provider.publishArticle = originalPublish; });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Run the publisher
|
|
135
|
+
await publisher({
|
|
136
|
+
Manager,
|
|
137
|
+
assistant,
|
|
138
|
+
context: {},
|
|
139
|
+
libraries: Manager.libraries,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Write metadata
|
|
143
|
+
jetpack.write(path.join(outDir, 'metadata.json'), JSON.stringify({
|
|
144
|
+
mode: 'extended',
|
|
145
|
+
platform: blogConfig.platform,
|
|
146
|
+
sources: content[0].sources,
|
|
147
|
+
published: !env.BLOG_NO_PUBLISH,
|
|
148
|
+
timestamp: new Date().toISOString(),
|
|
149
|
+
}, null, 2));
|
|
150
|
+
|
|
151
|
+
console.log(`\n[blog-generate] Done. Output: ${outDir}`);
|
|
152
|
+
|
|
153
|
+
// Auto-open (macOS)
|
|
154
|
+
if (env.BLOG_OPEN === '1' && process.platform === 'darwin') {
|
|
155
|
+
try { execSync(`open "${outDir}"`); } catch (e) { /* no-op */ }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
assert.ok(true, 'Blog auto-publisher completed');
|
|
159
|
+
},
|
|
160
|
+
};
|