@timmeck/brain-core 2.36.56 → 2.36.58
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 +6 -0
- package/command-center.html +133 -2
- package/dist/action/__tests__/action-bridge.test.d.ts +1 -0
- package/dist/action/__tests__/action-bridge.test.js +145 -0
- package/dist/action/__tests__/action-bridge.test.js.map +1 -0
- package/dist/action/__tests__/content-handler.test.d.ts +1 -0
- package/dist/action/__tests__/content-handler.test.js +100 -0
- package/dist/action/__tests__/content-handler.test.js.map +1 -0
- package/dist/action/__tests__/trade-handler.test.d.ts +1 -0
- package/dist/action/__tests__/trade-handler.test.js +165 -0
- package/dist/action/__tests__/trade-handler.test.js.map +1 -0
- package/dist/action/action-bridge.d.ts +95 -0
- package/dist/action/action-bridge.js +242 -0
- package/dist/action/action-bridge.js.map +1 -0
- package/dist/action/handlers/content-handler.d.ts +23 -0
- package/dist/action/handlers/content-handler.js +30 -0
- package/dist/action/handlers/content-handler.js.map +1 -0
- package/dist/action/handlers/index.d.ts +4 -0
- package/dist/action/handlers/index.js +3 -0
- package/dist/action/handlers/index.js.map +1 -0
- package/dist/action/handlers/trade-handler.d.ts +43 -0
- package/dist/action/handlers/trade-handler.js +43 -0
- package/dist/action/handlers/trade-handler.js.map +1 -0
- package/dist/action/index.d.ts +4 -0
- package/dist/action/index.js +3 -0
- package/dist/action/index.js.map +1 -0
- package/dist/agent-training/__tests__/agent-trainer.test.d.ts +1 -0
- package/dist/agent-training/__tests__/agent-trainer.test.js +158 -0
- package/dist/agent-training/__tests__/agent-trainer.test.js.map +1 -0
- package/dist/agent-training/__tests__/sub-agent-factory.test.d.ts +1 -0
- package/dist/agent-training/__tests__/sub-agent-factory.test.js +100 -0
- package/dist/agent-training/__tests__/sub-agent-factory.test.js.map +1 -0
- package/dist/agent-training/__tests__/sub-agent.test.d.ts +1 -0
- package/dist/agent-training/__tests__/sub-agent.test.js +102 -0
- package/dist/agent-training/__tests__/sub-agent.test.js.map +1 -0
- package/dist/agent-training/index.d.ts +4 -0
- package/dist/agent-training/index.js +2 -0
- package/dist/agent-training/index.js.map +1 -1
- package/dist/agent-training/sub-agent-factory.d.ts +36 -0
- package/dist/agent-training/sub-agent-factory.js +128 -0
- package/dist/agent-training/sub-agent-factory.js.map +1 -0
- package/dist/agent-training/sub-agent.d.ts +57 -0
- package/dist/agent-training/sub-agent.js +135 -0
- package/dist/agent-training/sub-agent.js.map +1 -0
- package/dist/chat/__tests__/chat-engine.test.d.ts +1 -0
- package/dist/chat/__tests__/chat-engine.test.js +128 -0
- package/dist/chat/__tests__/chat-engine.test.js.map +1 -0
- package/dist/chat/chat-engine.d.ts +55 -0
- package/dist/chat/chat-engine.js +186 -0
- package/dist/chat/chat-engine.js.map +1 -0
- package/dist/chat/index.d.ts +2 -0
- package/dist/chat/index.js +2 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/codegen/__tests__/code-forge.test.d.ts +1 -0
- package/dist/codegen/__tests__/code-forge.test.js +105 -0
- package/dist/codegen/__tests__/code-forge.test.js.map +1 -0
- package/dist/codegen/code-forge.d.ts +87 -0
- package/dist/codegen/code-forge.js +211 -0
- package/dist/codegen/code-forge.js.map +1 -0
- package/dist/codegen/index.d.ts +2 -0
- package/dist/codegen/index.js +1 -0
- package/dist/codegen/index.js.map +1 -1
- package/dist/content/__tests__/auto-publisher.test.d.ts +1 -0
- package/dist/content/__tests__/auto-publisher.test.js +125 -0
- package/dist/content/__tests__/auto-publisher.test.js.map +1 -0
- package/dist/content/__tests__/content-forge.test.d.ts +1 -0
- package/dist/content/__tests__/content-forge.test.js +117 -0
- package/dist/content/__tests__/content-forge.test.js.map +1 -0
- package/dist/content/auto-publisher.d.ts +51 -0
- package/dist/content/auto-publisher.js +147 -0
- package/dist/content/auto-publisher.js.map +1 -0
- package/dist/content/content-forge.d.ts +106 -0
- package/dist/content/content-forge.js +232 -0
- package/dist/content/content-forge.js.map +1 -0
- package/dist/content/index.d.ts +4 -0
- package/dist/content/index.js +3 -0
- package/dist/content/index.js.map +1 -0
- package/dist/creative/__tests__/creative-engine.test.d.ts +1 -0
- package/dist/creative/__tests__/creative-engine.test.js +151 -0
- package/dist/creative/__tests__/creative-engine.test.js.map +1 -0
- package/dist/cross-brain/__tests__/signal-router.test.d.ts +1 -0
- package/dist/cross-brain/__tests__/signal-router.test.js +159 -0
- package/dist/cross-brain/__tests__/signal-router.test.js.map +1 -0
- package/dist/cross-brain/signal-router.d.ts +57 -0
- package/dist/cross-brain/signal-router.js +148 -0
- package/dist/cross-brain/signal-router.js.map +1 -0
- package/dist/dashboard/__tests__/command-center-server.test.js +31 -0
- package/dist/dashboard/__tests__/command-center-server.test.js.map +1 -1
- package/dist/dashboard/command-center-server.d.ts +11 -0
- package/dist/dashboard/command-center-server.js +72 -0
- package/dist/dashboard/command-center-server.js.map +1 -1
- package/dist/dream/__tests__/dream-engine.test.d.ts +1 -0
- package/dist/dream/__tests__/dream-engine.test.js +184 -0
- package/dist/dream/__tests__/dream-engine.test.js.map +1 -0
- package/dist/feedback/__tests__/feedback-router.test.d.ts +1 -0
- package/dist/feedback/__tests__/feedback-router.test.js +173 -0
- package/dist/feedback/__tests__/feedback-router.test.js.map +1 -0
- package/dist/feedback/feedback-router.d.ts +53 -0
- package/dist/feedback/feedback-router.js +193 -0
- package/dist/feedback/feedback-router.js.map +1 -0
- package/dist/feedback/index.d.ts +2 -0
- package/dist/feedback/index.js +1 -0
- package/dist/feedback/index.js.map +1 -1
- package/dist/goals/__tests__/goal-engine.test.d.ts +1 -0
- package/dist/goals/__tests__/goal-engine.test.js +203 -0
- package/dist/goals/__tests__/goal-engine.test.js.map +1 -0
- package/dist/guardrails/__tests__/guardrail-engine.test.d.ts +1 -0
- package/dist/guardrails/__tests__/guardrail-engine.test.js +343 -0
- package/dist/guardrails/__tests__/guardrail-engine.test.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/metacognition/__tests__/auto-experiment-engine.test.d.ts +1 -0
- package/dist/metacognition/__tests__/auto-experiment-engine.test.js +218 -0
- package/dist/metacognition/__tests__/auto-experiment-engine.test.js.map +1 -0
- package/dist/metacognition/__tests__/evolution-engine.test.d.ts +1 -0
- package/dist/metacognition/__tests__/evolution-engine.test.js +473 -0
- package/dist/metacognition/__tests__/evolution-engine.test.js.map +1 -0
- package/dist/metacognition/__tests__/parameter-registry.test.d.ts +1 -0
- package/dist/metacognition/__tests__/parameter-registry.test.js +223 -0
- package/dist/metacognition/__tests__/parameter-registry.test.js.map +1 -0
- package/dist/prediction/__tests__/prediction-engine.test.d.ts +1 -0
- package/dist/prediction/__tests__/prediction-engine.test.js +244 -0
- package/dist/prediction/__tests__/prediction-engine.test.js.map +1 -0
- package/dist/research/research-orchestrator.d.ts +12 -0
- package/dist/research/research-orchestrator.js +109 -0
- package/dist/research/research-orchestrator.js.map +1 -1
- package/dist/sandbox/__tests__/code-sandbox.test.d.ts +1 -0
- package/dist/sandbox/__tests__/code-sandbox.test.js +182 -0
- package/dist/sandbox/__tests__/code-sandbox.test.js.map +1 -0
- package/dist/scanner/__tests__/signal-scanner.test.d.ts +1 -0
- package/dist/scanner/__tests__/signal-scanner.test.js +142 -0
- package/dist/scanner/__tests__/signal-scanner.test.js.map +1 -0
- package/dist/self-modification/__tests__/self-modification-engine.test.d.ts +1 -0
- package/dist/self-modification/__tests__/self-modification-engine.test.js +255 -0
- package/dist/self-modification/__tests__/self-modification-engine.test.js.map +1 -0
- package/dist/strategy/__tests__/strategy-forge.test.d.ts +1 -0
- package/dist/strategy/__tests__/strategy-forge.test.js +190 -0
- package/dist/strategy/__tests__/strategy-forge.test.js.map +1 -0
- package/dist/strategy/__tests__/strategy-mutator.test.d.ts +1 -0
- package/dist/strategy/__tests__/strategy-mutator.test.js +141 -0
- package/dist/strategy/__tests__/strategy-mutator.test.js.map +1 -0
- package/dist/strategy/index.d.ts +4 -0
- package/dist/strategy/index.js +3 -0
- package/dist/strategy/index.js.map +1 -0
- package/dist/strategy/strategy-forge.d.ts +114 -0
- package/dist/strategy/strategy-forge.js +296 -0
- package/dist/strategy/strategy-forge.js.map +1 -0
- package/dist/strategy/strategy-mutator.d.ts +42 -0
- package/dist/strategy/strategy-mutator.js +140 -0
- package/dist/strategy/strategy-mutator.js.map +1 -0
- package/package.json +1 -1
package/dist/codegen/index.js
CHANGED
|
@@ -6,4 +6,5 @@ export { CodegenServer } from './codegen-server.js';
|
|
|
6
6
|
export { RepoAbsorber } from './repo-absorber.js';
|
|
7
7
|
export { FeatureExtractor } from './feature-extractor.js';
|
|
8
8
|
export { FeatureRecommender } from './feature-recommender.js';
|
|
9
|
+
export { CodeForge, runCodeForgeMigration } from './code-forge.js';
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/codegen/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/codegen/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
vi.mock('../../utils/logger.js', () => ({
|
|
4
|
+
getLogger: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }),
|
|
5
|
+
}));
|
|
6
|
+
import { ContentForge } from '../content-forge.js';
|
|
7
|
+
import { AutoPublisher } from '../auto-publisher.js';
|
|
8
|
+
describe('AutoPublisher', () => {
|
|
9
|
+
let db;
|
|
10
|
+
let forge;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
db = new Database(':memory:');
|
|
13
|
+
forge = new ContentForge(db, { brainName: 'test' });
|
|
14
|
+
});
|
|
15
|
+
afterEach(() => { db.close(); });
|
|
16
|
+
it('creates with default config', () => {
|
|
17
|
+
const pub = new AutoPublisher(forge);
|
|
18
|
+
const stats = pub.getStats();
|
|
19
|
+
expect(stats.publishedToday).toBe(0);
|
|
20
|
+
expect(stats.lastPublishAt).toBeNull();
|
|
21
|
+
expect(stats.checksRun).toBe(0);
|
|
22
|
+
});
|
|
23
|
+
it('checkAndPublish returns 0 when no scheduled content', async () => {
|
|
24
|
+
const pub = new AutoPublisher(forge);
|
|
25
|
+
const result = await pub.checkAndPublish();
|
|
26
|
+
expect(result.published).toBe(0);
|
|
27
|
+
expect(result.skipped).toBe(0);
|
|
28
|
+
});
|
|
29
|
+
it('publishes scheduled content when due', async () => {
|
|
30
|
+
const mockSocial = { post: vi.fn().mockResolvedValue({ id: 'post-1' }) };
|
|
31
|
+
forge.setSocialService(mockSocial);
|
|
32
|
+
const piece = forge.generateFromInsight({ insight: 'Test insight for publishing', noveltyScore: 0.8 });
|
|
33
|
+
// Schedule for the past so it's due
|
|
34
|
+
forge.schedule(piece.id, '2020-01-01T00:00:00Z');
|
|
35
|
+
const pub = new AutoPublisher(forge);
|
|
36
|
+
const result = await pub.checkAndPublish();
|
|
37
|
+
expect(result.published).toBe(1);
|
|
38
|
+
expect(mockSocial.post).toHaveBeenCalledOnce();
|
|
39
|
+
});
|
|
40
|
+
it('skips future-scheduled content', async () => {
|
|
41
|
+
forge.setSocialService({ post: vi.fn().mockResolvedValue({ id: 'post-1' }) });
|
|
42
|
+
const piece = forge.generateFromInsight({ insight: 'Future content', noveltyScore: 0.8 });
|
|
43
|
+
forge.schedule(piece.id, '2099-12-31T23:59:59Z');
|
|
44
|
+
const pub = new AutoPublisher(forge);
|
|
45
|
+
const result = await pub.checkAndPublish();
|
|
46
|
+
expect(result.published).toBe(0);
|
|
47
|
+
expect(result.skipped).toBe(1);
|
|
48
|
+
});
|
|
49
|
+
it('respects rate limit per hour', async () => {
|
|
50
|
+
forge.setSocialService({ post: vi.fn().mockResolvedValue({ id: 'post-1' }) });
|
|
51
|
+
const p1 = forge.generateFromInsight({ insight: 'Content 1', noveltyScore: 0.8 });
|
|
52
|
+
const p2 = forge.generateFromInsight({ insight: 'Content 2', noveltyScore: 0.8 });
|
|
53
|
+
forge.schedule(p1.id, '2020-01-01T00:00:00Z');
|
|
54
|
+
forge.schedule(p2.id, '2020-01-01T00:00:00Z');
|
|
55
|
+
const pub = new AutoPublisher(forge, { maxPublishPerHour: 1, minTimeBetweenPostsMs: 0, engagementCheckDelayMs: 3600000, requireLLMPolish: false });
|
|
56
|
+
const result = await pub.checkAndPublish();
|
|
57
|
+
expect(result.published).toBe(1);
|
|
58
|
+
});
|
|
59
|
+
it('handles publish failures gracefully', async () => {
|
|
60
|
+
forge.setSocialService({ post: vi.fn().mockRejectedValue(new Error('API down')) });
|
|
61
|
+
const piece = forge.generateFromInsight({ insight: 'Will fail', noveltyScore: 0.8 });
|
|
62
|
+
forge.schedule(piece.id, '2020-01-01T00:00:00Z');
|
|
63
|
+
const pub = new AutoPublisher(forge);
|
|
64
|
+
const result = await pub.checkAndPublish();
|
|
65
|
+
expect(result.published).toBe(0);
|
|
66
|
+
expect(result.skipped).toBe(1);
|
|
67
|
+
});
|
|
68
|
+
it('auto-schedules drafts at optimal time', async () => {
|
|
69
|
+
forge.setSocialService({ post: vi.fn().mockResolvedValue({ id: 'post-1' }) });
|
|
70
|
+
// Create a draft but don't schedule it
|
|
71
|
+
forge.generateFromInsight({ insight: 'Unscheduled draft content piece', noveltyScore: 0.8 });
|
|
72
|
+
const pub = new AutoPublisher(forge);
|
|
73
|
+
await pub.checkAndPublish();
|
|
74
|
+
// Draft should now be scheduled
|
|
75
|
+
const scheduled = forge.getSchedule();
|
|
76
|
+
expect(scheduled.length).toBeGreaterThanOrEqual(1);
|
|
77
|
+
});
|
|
78
|
+
it('tracks stats correctly', async () => {
|
|
79
|
+
forge.setSocialService({ post: vi.fn().mockResolvedValue({ id: 'post-1' }) });
|
|
80
|
+
const piece = forge.generateFromInsight({ insight: 'Stats test', noveltyScore: 0.8 });
|
|
81
|
+
forge.schedule(piece.id, '2020-01-01T00:00:00Z');
|
|
82
|
+
const pub = new AutoPublisher(forge, { maxPublishPerHour: 5, minTimeBetweenPostsMs: 0, engagementCheckDelayMs: 100, requireLLMPolish: false });
|
|
83
|
+
await pub.checkAndPublish();
|
|
84
|
+
const stats = pub.getStats();
|
|
85
|
+
expect(stats.publishedToday).toBe(1);
|
|
86
|
+
expect(stats.lastPublishAt).not.toBeNull();
|
|
87
|
+
expect(stats.checksRun).toBe(1);
|
|
88
|
+
});
|
|
89
|
+
it('resetDaily clears daily count', async () => {
|
|
90
|
+
forge.setSocialService({ post: vi.fn().mockResolvedValue({ id: 'post-1' }) });
|
|
91
|
+
const piece = forge.generateFromInsight({ insight: 'Reset test', noveltyScore: 0.8 });
|
|
92
|
+
forge.schedule(piece.id, '2020-01-01T00:00:00Z');
|
|
93
|
+
const pub = new AutoPublisher(forge, { maxPublishPerHour: 5, minTimeBetweenPostsMs: 0, engagementCheckDelayMs: 100, requireLLMPolish: false });
|
|
94
|
+
await pub.checkAndPublish();
|
|
95
|
+
expect(pub.getStats().publishedToday).toBe(1);
|
|
96
|
+
pub.resetDaily();
|
|
97
|
+
expect(pub.getStats().publishedToday).toBe(0);
|
|
98
|
+
});
|
|
99
|
+
it('refreshEngagement processes due checks', async () => {
|
|
100
|
+
const pub = new AutoPublisher(forge, { maxPublishPerHour: 5, minTimeBetweenPostsMs: 0, engagementCheckDelayMs: 0, requireLLMPolish: false });
|
|
101
|
+
forge.setSocialService({ post: vi.fn().mockResolvedValue({ id: 'post-1' }) });
|
|
102
|
+
const piece = forge.generateFromInsight({ insight: 'Engagement test', noveltyScore: 0.8 });
|
|
103
|
+
forge.schedule(piece.id, '2020-01-01T00:00:00Z');
|
|
104
|
+
await pub.checkAndPublish();
|
|
105
|
+
// With engagementCheckDelayMs: 0, the check should be due immediately
|
|
106
|
+
const refreshed = await pub.refreshEngagement();
|
|
107
|
+
expect(refreshed).toBeGreaterThanOrEqual(1);
|
|
108
|
+
});
|
|
109
|
+
it('start and stop manage hourly timer', () => {
|
|
110
|
+
const pub = new AutoPublisher(forge);
|
|
111
|
+
pub.start();
|
|
112
|
+
pub.stop();
|
|
113
|
+
// No error = success
|
|
114
|
+
});
|
|
115
|
+
it('handles no social service gracefully', async () => {
|
|
116
|
+
// No social service set → publishNow returns { success: false }
|
|
117
|
+
const piece = forge.generateFromInsight({ insight: 'No social', noveltyScore: 0.8 });
|
|
118
|
+
forge.schedule(piece.id, '2020-01-01T00:00:00Z');
|
|
119
|
+
const pub = new AutoPublisher(forge);
|
|
120
|
+
const result = await pub.checkAndPublish();
|
|
121
|
+
expect(result.published).toBe(0);
|
|
122
|
+
expect(result.skipped).toBe(1);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=auto-publisher.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-publisher.test.js","sourceRoot":"","sources":["../../../src/content/__tests__/auto-publisher.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
vi.mock('../../utils/logger.js', () => ({
|
|
4
|
+
getLogger: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }),
|
|
5
|
+
}));
|
|
6
|
+
import { ContentForge, runContentForgeMigration } from '../content-forge.js';
|
|
7
|
+
describe('ContentForge', () => {
|
|
8
|
+
let db;
|
|
9
|
+
beforeEach(() => { db = new Database(':memory:'); });
|
|
10
|
+
afterEach(() => { db.close(); });
|
|
11
|
+
it('generates content from an insight', () => {
|
|
12
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
13
|
+
const piece = forge.generateFromInsight({ id: 1, insight: 'Cross-domain patterns emerge between trading and marketing signals', noveltyScore: 0.8 });
|
|
14
|
+
expect(piece.id).toBeGreaterThan(0);
|
|
15
|
+
expect(piece.sourceType).toBe('insight');
|
|
16
|
+
expect(piece.status).toBe('draft');
|
|
17
|
+
expect(piece.platform).toBe('bluesky');
|
|
18
|
+
});
|
|
19
|
+
it('generates content from a mission', () => {
|
|
20
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
21
|
+
const piece = forge.generateFromMission({ id: 5, topic: 'AI agent architectures', summary: 'Survey of modern agent designs' });
|
|
22
|
+
expect(piece.sourceType).toBe('mission');
|
|
23
|
+
expect(piece.title).toContain('Research');
|
|
24
|
+
});
|
|
25
|
+
it('generates content from a trend', () => {
|
|
26
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
27
|
+
const piece = forge.generateFromTrend({ name: 'WebAssembly', description: 'WASM growing in backend usage', category: 'infrastructure' });
|
|
28
|
+
expect(piece.sourceType).toBe('trend');
|
|
29
|
+
expect(piece.title).toContain('Trend');
|
|
30
|
+
});
|
|
31
|
+
it('generates content from a principle', () => {
|
|
32
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
33
|
+
const piece = forge.generateFromPrinciple({ id: 3, statement: 'Early feedback loops reduce cascading failures', domain: 'engineering' });
|
|
34
|
+
expect(piece.sourceType).toBe('principle');
|
|
35
|
+
});
|
|
36
|
+
it('schedules content for later', () => {
|
|
37
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
38
|
+
const piece = forge.generateFromInsight({ insight: 'Test insight', noveltyScore: 0.5 });
|
|
39
|
+
forge.schedule(piece.id, '2026-03-15T10:00:00Z');
|
|
40
|
+
const scheduled = forge.getSchedule();
|
|
41
|
+
expect(scheduled).toHaveLength(1);
|
|
42
|
+
expect(scheduled[0].scheduledFor).toBe('2026-03-15T10:00:00Z');
|
|
43
|
+
expect(scheduled[0].status).toBe('scheduled');
|
|
44
|
+
});
|
|
45
|
+
it('publishes content via social service', async () => {
|
|
46
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
47
|
+
forge.setSocialService({ post: vi.fn().mockResolvedValue({ id: 'post-123' }) });
|
|
48
|
+
const piece = forge.generateFromInsight({ insight: 'Publish me', noveltyScore: 0.9 });
|
|
49
|
+
const result = await forge.publishNow(piece.id);
|
|
50
|
+
expect(result.success).toBe(true);
|
|
51
|
+
expect(result.postId).toBe('post-123');
|
|
52
|
+
const updated = forge.getPiece(piece.id);
|
|
53
|
+
expect(updated?.status).toBe('published');
|
|
54
|
+
});
|
|
55
|
+
it('handles publish failure', async () => {
|
|
56
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
57
|
+
forge.setSocialService({ post: vi.fn().mockRejectedValue(new Error('Rate limited')) });
|
|
58
|
+
const piece = forge.generateFromInsight({ insight: 'Fail publish', noveltyScore: 0.5 });
|
|
59
|
+
const result = await forge.publishNow(piece.id);
|
|
60
|
+
expect(result.success).toBe(false);
|
|
61
|
+
const updated = forge.getPiece(piece.id);
|
|
62
|
+
expect(updated?.status).toBe('failed');
|
|
63
|
+
});
|
|
64
|
+
it('returns failure when no social service', async () => {
|
|
65
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
66
|
+
const piece = forge.generateFromInsight({ insight: 'No service', noveltyScore: 0.5 });
|
|
67
|
+
const result = await forge.publishNow(piece.id);
|
|
68
|
+
expect(result.success).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
it('records engagement metrics', () => {
|
|
71
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
72
|
+
forge.setSocialService({ post: vi.fn().mockResolvedValue({ id: 'p1' }) });
|
|
73
|
+
const piece = forge.generateFromInsight({ insight: 'Engage me', noveltyScore: 0.7 });
|
|
74
|
+
forge.recordEngagement(piece.id, { likes: 42, reposts: 10, replies: 5 });
|
|
75
|
+
const updated = forge.getPiece(piece.id);
|
|
76
|
+
expect(updated?.engagement?.likes).toBe(42);
|
|
77
|
+
});
|
|
78
|
+
it('gets best performing content', async () => {
|
|
79
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
80
|
+
forge.setSocialService({ post: vi.fn().mockResolvedValue({ id: 'p1' }) });
|
|
81
|
+
const p1 = forge.generateFromInsight({ insight: 'Popular', noveltyScore: 0.9 });
|
|
82
|
+
const p2 = forge.generateFromInsight({ insight: 'Less popular', noveltyScore: 0.5 });
|
|
83
|
+
await forge.publishNow(p1.id);
|
|
84
|
+
await forge.publishNow(p2.id);
|
|
85
|
+
forge.recordEngagement(p1.id, { likes: 100, reposts: 50, replies: 20 });
|
|
86
|
+
forge.recordEngagement(p2.id, { likes: 5, reposts: 1, replies: 0 });
|
|
87
|
+
const best = forge.getBestPerforming(2);
|
|
88
|
+
expect(best).toHaveLength(2);
|
|
89
|
+
expect(best[0].engagement?.likes).toBe(100);
|
|
90
|
+
});
|
|
91
|
+
it('returns optimal posting time', () => {
|
|
92
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
93
|
+
expect(forge.getOptimalTime('bluesky')).toBe('10:00');
|
|
94
|
+
expect(forge.getOptimalTime('reddit')).toBe('08:00');
|
|
95
|
+
});
|
|
96
|
+
it('getStatus returns overview', () => {
|
|
97
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
98
|
+
forge.generateFromInsight({ insight: 'Draft 1', noveltyScore: 0.5 });
|
|
99
|
+
forge.generateFromInsight({ insight: 'Draft 2', noveltyScore: 0.6 });
|
|
100
|
+
const status = forge.getStatus();
|
|
101
|
+
expect(status.drafts).toBe(2);
|
|
102
|
+
expect(status.published).toBe(0);
|
|
103
|
+
});
|
|
104
|
+
it('migration is idempotent', () => {
|
|
105
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
106
|
+
forge.generateFromInsight({ insight: 'Survives', noveltyScore: 0.5 });
|
|
107
|
+
runContentForgeMigration(db);
|
|
108
|
+
const pieces = forge.getByStatus('draft');
|
|
109
|
+
expect(pieces).toHaveLength(1);
|
|
110
|
+
});
|
|
111
|
+
it('uses custom platform', () => {
|
|
112
|
+
const forge = new ContentForge(db, { brainName: 'test' });
|
|
113
|
+
const piece = forge.generateFromInsight({ insight: 'Reddit post', noveltyScore: 0.7 }, 'reddit');
|
|
114
|
+
expect(piece.platform).toBe('reddit');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=content-forge.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-forge.test.js","sourceRoot":"","sources":["../../../src/content/__tests__/content-forge.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ContentForge } from './content-forge.js';
|
|
2
|
+
export interface AutoPublisherConfig {
|
|
3
|
+
maxPublishPerHour: number;
|
|
4
|
+
minTimeBetweenPostsMs: number;
|
|
5
|
+
engagementCheckDelayMs: number;
|
|
6
|
+
requireLLMPolish: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface AutoPublisherStats {
|
|
9
|
+
publishedToday: number;
|
|
10
|
+
avgEngagement: number;
|
|
11
|
+
lastPublishAt: string | null;
|
|
12
|
+
checksRun: number;
|
|
13
|
+
}
|
|
14
|
+
export declare class AutoPublisher {
|
|
15
|
+
private contentForge;
|
|
16
|
+
private readonly config;
|
|
17
|
+
private publishedThisHour;
|
|
18
|
+
private lastPublishAt;
|
|
19
|
+
private publishedToday;
|
|
20
|
+
private checksRun;
|
|
21
|
+
private hourResetTimer;
|
|
22
|
+
private pendingEngagementChecks;
|
|
23
|
+
constructor(contentForge: ContentForge, config?: Partial<AutoPublisherConfig>);
|
|
24
|
+
/**
|
|
25
|
+
* Check for due scheduled content and publish it.
|
|
26
|
+
* Returns count of published and skipped pieces.
|
|
27
|
+
*/
|
|
28
|
+
checkAndPublish(): Promise<{
|
|
29
|
+
published: number;
|
|
30
|
+
skipped: number;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Check engagement for recently published content.
|
|
34
|
+
* Calls the provided refresh function for each piece whose check time has arrived.
|
|
35
|
+
*/
|
|
36
|
+
refreshEngagement(socialService?: {
|
|
37
|
+
getEngagement?: (postId: string) => Promise<{
|
|
38
|
+
likes: number;
|
|
39
|
+
reposts: number;
|
|
40
|
+
replies: number;
|
|
41
|
+
}>;
|
|
42
|
+
}): Promise<number>;
|
|
43
|
+
/** Start hourly rate-limit reset timer */
|
|
44
|
+
start(): void;
|
|
45
|
+
/** Stop timer */
|
|
46
|
+
stop(): void;
|
|
47
|
+
/** Reset daily counter (call at midnight) */
|
|
48
|
+
resetDaily(): void;
|
|
49
|
+
/** Get statistics */
|
|
50
|
+
getStats(): AutoPublisherStats;
|
|
51
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
const DEFAULT_CONFIG = {
|
|
3
|
+
maxPublishPerHour: 2,
|
|
4
|
+
minTimeBetweenPostsMs: 1_800_000, // 30 min
|
|
5
|
+
engagementCheckDelayMs: 3_600_000, // 1 hour
|
|
6
|
+
requireLLMPolish: false,
|
|
7
|
+
};
|
|
8
|
+
// ── AutoPublisher ──────────────────────────────────────────
|
|
9
|
+
const log = getLogger();
|
|
10
|
+
export class AutoPublisher {
|
|
11
|
+
contentForge;
|
|
12
|
+
config;
|
|
13
|
+
publishedThisHour = 0;
|
|
14
|
+
lastPublishAt = 0;
|
|
15
|
+
publishedToday = 0;
|
|
16
|
+
checksRun = 0;
|
|
17
|
+
hourResetTimer = null;
|
|
18
|
+
pendingEngagementChecks = [];
|
|
19
|
+
constructor(contentForge, config) {
|
|
20
|
+
this.contentForge = contentForge;
|
|
21
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Check for due scheduled content and publish it.
|
|
25
|
+
* Returns count of published and skipped pieces.
|
|
26
|
+
*/
|
|
27
|
+
async checkAndPublish() {
|
|
28
|
+
this.checksRun++;
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
let published = 0;
|
|
31
|
+
let skipped = 0;
|
|
32
|
+
// Rate limit: max per hour
|
|
33
|
+
if (this.publishedThisHour >= this.config.maxPublishPerHour) {
|
|
34
|
+
log.info(`[auto-publisher] Rate limit reached (${this.publishedThisHour}/${this.config.maxPublishPerHour} per hour)`);
|
|
35
|
+
return { published: 0, skipped: 0 };
|
|
36
|
+
}
|
|
37
|
+
// Rate limit: min time between posts
|
|
38
|
+
if (now - this.lastPublishAt < this.config.minTimeBetweenPostsMs) {
|
|
39
|
+
return { published: 0, skipped: 0 };
|
|
40
|
+
}
|
|
41
|
+
// Get scheduled content that's due
|
|
42
|
+
const scheduled = this.contentForge.getSchedule();
|
|
43
|
+
const nowIso = new Date().toISOString();
|
|
44
|
+
for (const piece of scheduled) {
|
|
45
|
+
if (this.publishedThisHour >= this.config.maxPublishPerHour)
|
|
46
|
+
break;
|
|
47
|
+
if (now - this.lastPublishAt < this.config.minTimeBetweenPostsMs && published > 0)
|
|
48
|
+
break;
|
|
49
|
+
// Only publish if scheduled time has passed
|
|
50
|
+
if (piece.scheduledFor && piece.scheduledFor > nowIso) {
|
|
51
|
+
skipped++;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const result = await this.contentForge.publishNow(piece.id);
|
|
56
|
+
if (result.success) {
|
|
57
|
+
published++;
|
|
58
|
+
this.publishedThisHour++;
|
|
59
|
+
this.publishedToday++;
|
|
60
|
+
this.lastPublishAt = Date.now();
|
|
61
|
+
// Schedule engagement check
|
|
62
|
+
this.pendingEngagementChecks.push({
|
|
63
|
+
pieceId: piece.id,
|
|
64
|
+
checkAt: Date.now() + this.config.engagementCheckDelayMs,
|
|
65
|
+
});
|
|
66
|
+
log.info(`[auto-publisher] Published #${piece.id}: ${piece.title} → ${result.postId ?? 'ok'}`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
skipped++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
log.warn(`[auto-publisher] Failed to publish #${piece.id}: ${err.message}`);
|
|
74
|
+
skipped++;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Also publish high-confidence drafts (not yet scheduled)
|
|
78
|
+
const drafts = this.contentForge.getByStatus('draft', 5);
|
|
79
|
+
for (const draft of drafts) {
|
|
80
|
+
if (this.publishedThisHour >= this.config.maxPublishPerHour)
|
|
81
|
+
break;
|
|
82
|
+
if (now - this.lastPublishAt < this.config.minTimeBetweenPostsMs && published > 0)
|
|
83
|
+
break;
|
|
84
|
+
// Auto-schedule draft at optimal time
|
|
85
|
+
const optimalTime = this.contentForge.getOptimalTime(draft.platform);
|
|
86
|
+
const today = new Date().toISOString().split('T')[0];
|
|
87
|
+
this.contentForge.schedule(draft.id, `${today}T${optimalTime}:00Z`);
|
|
88
|
+
skipped++; // Scheduled for later, not published now
|
|
89
|
+
}
|
|
90
|
+
return { published, skipped };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Check engagement for recently published content.
|
|
94
|
+
* Calls the provided refresh function for each piece whose check time has arrived.
|
|
95
|
+
*/
|
|
96
|
+
async refreshEngagement(socialService) {
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
const due = this.pendingEngagementChecks.filter(c => c.checkAt <= now);
|
|
99
|
+
let refreshed = 0;
|
|
100
|
+
for (const check of due) {
|
|
101
|
+
try {
|
|
102
|
+
const piece = this.contentForge.getPiece(check.pieceId);
|
|
103
|
+
if (piece && piece.status === 'published') {
|
|
104
|
+
// If we have a social service with getEngagement, use it
|
|
105
|
+
if (socialService?.getEngagement) {
|
|
106
|
+
// We'd need the postId — for now just record placeholder
|
|
107
|
+
log.info(`[auto-publisher] Engagement check for #${check.pieceId} scheduled`);
|
|
108
|
+
}
|
|
109
|
+
refreshed++;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
log.warn(`[auto-publisher] Engagement refresh error for #${check.pieceId}: ${err.message}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Remove processed checks
|
|
117
|
+
this.pendingEngagementChecks = this.pendingEngagementChecks.filter(c => c.checkAt > now);
|
|
118
|
+
return refreshed;
|
|
119
|
+
}
|
|
120
|
+
/** Start hourly rate-limit reset timer */
|
|
121
|
+
start() {
|
|
122
|
+
this.hourResetTimer = setInterval(() => {
|
|
123
|
+
this.publishedThisHour = 0;
|
|
124
|
+
}, 3_600_000);
|
|
125
|
+
}
|
|
126
|
+
/** Stop timer */
|
|
127
|
+
stop() {
|
|
128
|
+
if (this.hourResetTimer) {
|
|
129
|
+
clearInterval(this.hourResetTimer);
|
|
130
|
+
this.hourResetTimer = null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** Reset daily counter (call at midnight) */
|
|
134
|
+
resetDaily() {
|
|
135
|
+
this.publishedToday = 0;
|
|
136
|
+
}
|
|
137
|
+
/** Get statistics */
|
|
138
|
+
getStats() {
|
|
139
|
+
return {
|
|
140
|
+
publishedToday: this.publishedToday,
|
|
141
|
+
avgEngagement: this.contentForge.getStatus().avgEngagement,
|
|
142
|
+
lastPublishAt: this.lastPublishAt > 0 ? new Date(this.lastPublishAt).toISOString() : null,
|
|
143
|
+
checksRun: this.checksRun,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=auto-publisher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-publisher.js","sourceRoot":"","sources":["../../src/content/auto-publisher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
export interface ContentPiece {
|
|
3
|
+
id: number;
|
|
4
|
+
sourceType: 'insight' | 'mission' | 'trend' | 'principle' | 'manual';
|
|
5
|
+
sourceId?: number;
|
|
6
|
+
platform: 'bluesky' | 'reddit' | 'telegram' | 'discord';
|
|
7
|
+
title: string;
|
|
8
|
+
body: string;
|
|
9
|
+
hashtags: string[];
|
|
10
|
+
scheduledFor?: string;
|
|
11
|
+
status: 'draft' | 'scheduled' | 'published' | 'failed';
|
|
12
|
+
engagement?: ContentEngagement;
|
|
13
|
+
createdAt?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ContentEngagement {
|
|
16
|
+
likes: number;
|
|
17
|
+
reposts: number;
|
|
18
|
+
replies: number;
|
|
19
|
+
}
|
|
20
|
+
export interface ContentForgeConfig {
|
|
21
|
+
brainName: string;
|
|
22
|
+
defaultPlatform?: ContentPiece['platform'];
|
|
23
|
+
maxDraftsPerCycle?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface ContentForgeStatus {
|
|
26
|
+
drafts: number;
|
|
27
|
+
scheduled: number;
|
|
28
|
+
published: number;
|
|
29
|
+
avgEngagement: number;
|
|
30
|
+
}
|
|
31
|
+
export declare function runContentForgeMigration(db: Database.Database): void;
|
|
32
|
+
export declare class ContentForge {
|
|
33
|
+
private readonly db;
|
|
34
|
+
private readonly config;
|
|
35
|
+
private readonly log;
|
|
36
|
+
private llmService;
|
|
37
|
+
private socialService;
|
|
38
|
+
private actionBridge;
|
|
39
|
+
private readonly stmtInsert;
|
|
40
|
+
private readonly stmtGetById;
|
|
41
|
+
private readonly stmtUpdateStatus;
|
|
42
|
+
private readonly stmtSetSchedule;
|
|
43
|
+
private readonly stmtSetEngagement;
|
|
44
|
+
private readonly stmtGetByStatus;
|
|
45
|
+
private readonly stmtGetSchedule;
|
|
46
|
+
private readonly stmtGetBest;
|
|
47
|
+
private readonly stmtCountByStatus;
|
|
48
|
+
private readonly stmtAvgEngagement;
|
|
49
|
+
constructor(db: Database.Database, config: ContentForgeConfig);
|
|
50
|
+
setLLMService(service: any): void;
|
|
51
|
+
setSocialService(service: {
|
|
52
|
+
post: (platform: string, content: string) => Promise<{
|
|
53
|
+
id: string;
|
|
54
|
+
}>;
|
|
55
|
+
}): void;
|
|
56
|
+
setActionBridge(bridge: import('../action/action-bridge.js').ActionBridgeEngine): void;
|
|
57
|
+
/** Generate content from an insight */
|
|
58
|
+
generateFromInsight(insight: {
|
|
59
|
+
id?: number;
|
|
60
|
+
insight: string;
|
|
61
|
+
noveltyScore: number;
|
|
62
|
+
}, platform?: ContentPiece['platform']): ContentPiece;
|
|
63
|
+
/** Generate content from a mission report */
|
|
64
|
+
generateFromMission(mission: {
|
|
65
|
+
id?: number;
|
|
66
|
+
topic: string;
|
|
67
|
+
summary?: string;
|
|
68
|
+
}, platform?: ContentPiece['platform']): ContentPiece;
|
|
69
|
+
/** Generate content from a tech trend */
|
|
70
|
+
generateFromTrend(trend: {
|
|
71
|
+
name: string;
|
|
72
|
+
description?: string;
|
|
73
|
+
category?: string;
|
|
74
|
+
}, platform?: ContentPiece['platform']): ContentPiece;
|
|
75
|
+
/** Generate content from a principle */
|
|
76
|
+
generateFromPrinciple(principle: {
|
|
77
|
+
id?: number;
|
|
78
|
+
statement: string;
|
|
79
|
+
domain?: string;
|
|
80
|
+
}, platform?: ContentPiece['platform']): ContentPiece;
|
|
81
|
+
/** Schedule a piece for later publication */
|
|
82
|
+
schedule(pieceId: number, when: string): void;
|
|
83
|
+
/** Publish immediately */
|
|
84
|
+
publishNow(pieceId: number): Promise<{
|
|
85
|
+
success: boolean;
|
|
86
|
+
postId?: string;
|
|
87
|
+
}>;
|
|
88
|
+
/** Get scheduled content */
|
|
89
|
+
getSchedule(): ContentPiece[];
|
|
90
|
+
/** Record engagement metrics */
|
|
91
|
+
recordEngagement(pieceId: number, metrics: ContentEngagement): void;
|
|
92
|
+
/** Get best performing published content */
|
|
93
|
+
getBestPerforming(limit?: number): ContentPiece[];
|
|
94
|
+
/** Get optimal posting time for a platform (placeholder — learns from engagement data) */
|
|
95
|
+
getOptimalTime(platform: string): string;
|
|
96
|
+
/** Get content pieces by status */
|
|
97
|
+
getByStatus(status: string, limit?: number): ContentPiece[];
|
|
98
|
+
/** Get a single piece by ID */
|
|
99
|
+
getPiece(id: number): ContentPiece | null;
|
|
100
|
+
/** Auto-schedule a piece at optimal time and create ActionBridge publish proposal */
|
|
101
|
+
autoScheduleAndPublish(pieceId: number): void;
|
|
102
|
+
/** Get status overview */
|
|
103
|
+
getStatus(): ContentForgeStatus;
|
|
104
|
+
private storePiece;
|
|
105
|
+
private extractHashtags;
|
|
106
|
+
}
|