@zzclub/pipeline 0.10.2 → 0.12.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 +8 -72
- package/package.json +1 -1
- package/src/adapter-types.ts +5 -0
- package/src/adapters/builtin-image-renderer.ts +7 -6
- package/src/adapters/builtin-markdown-renderer.ts +3 -0
- package/src/cli.ts +10 -4
- package/src/command-outcome.ts +50 -0
- package/src/commands/abandon.ts +13 -2
- package/src/commands/checkpoint.ts +3 -2
- package/src/commands/config.ts +47 -20
- package/src/commands/monitor.ts +21 -0
- package/src/commands/prepare-finalize.ts +2 -0
- package/src/commands/prepare.ts +3 -0
- package/src/commands/publish.ts +5 -2
- package/src/commands/render.ts +7 -3
- package/src/commands/republish.ts +4 -1
- package/src/commands/sync-blog.ts +4 -2
- package/src/commands/wechat-export.ts +6 -3
- package/src/config.ts +21 -2
- package/src/imgx/render-article.ts +8 -1
- package/src/imgx/render-card.ts +6 -1
- package/src/imgx/runtime.ts +1 -1
- package/src/logger.test.ts +28 -1
- package/src/logger.ts +39 -26
- package/src/monitor/client.ts +60 -0
- package/src/monitor/recorder.ts +142 -0
- package/src/monitor/runtime.ts +58 -0
- package/src/monitor/server.ts +170 -0
- package/src/monitor/store.ts +235 -0
- package/src/monitor/types.ts +77 -0
- package/src/monitor.test.ts +241 -0
- package/src/plugins.ts +4 -5
- package/src/providers/blog.ts +4 -1
- package/src/providers/cos.ts +3 -0
- package/src/providers/index.ts +7 -3
- package/src/providers/publish-core.ts +7 -1
- package/src/providers/wechat.ts +7 -2
- package/src/schema/config.account-name.test.ts +79 -0
- package/src/schema/config.ts +9 -3
- package/src/spawn.ts +4 -2
- package/src/state.ts +4 -0
- package/src/workflow.test.ts +3 -2
- package/src/commands/analytics.test.ts +0 -156
- package/src/commands/analytics.ts +0 -240
- package/src/commands/topic.test.ts +0 -146
- package/src/commands/topic.ts +0 -717
- package/src/db.test.ts +0 -47
- package/src/db.ts +0 -95
- package/src/schema/analytics.test.ts +0 -54
- package/src/schema/analytics.ts +0 -19
- package/src/schema/topic.test.ts +0 -69
- package/src/schema/topic.ts +0 -55
package/src/db.test.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
2
|
-
import { mkdtemp, rm, exists } from "fs/promises";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
import { tmpdir } from "os";
|
|
5
|
-
import { getDb, initDb } from "./db";
|
|
6
|
-
|
|
7
|
-
describe("db", () => {
|
|
8
|
-
let workspace: string;
|
|
9
|
-
|
|
10
|
-
beforeEach(async () => {
|
|
11
|
-
workspace = await mkdtemp(join(tmpdir(), "zzhub-test-"));
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
afterEach(async () => {
|
|
15
|
-
await rm(workspace, { recursive: true, force: true });
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test("getDb creates database file", () => {
|
|
19
|
-
const db = getDb(workspace);
|
|
20
|
-
db.close();
|
|
21
|
-
expect(exists(join(workspace, "zzhub.db"))).resolves.toBe(true);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test("initDb creates topics table", () => {
|
|
25
|
-
const db = getDb(workspace);
|
|
26
|
-
initDb(db);
|
|
27
|
-
|
|
28
|
-
const tables = db.prepare(
|
|
29
|
-
"SELECT name FROM sqlite_master WHERE type='table' AND name='topics'"
|
|
30
|
-
).all();
|
|
31
|
-
|
|
32
|
-
expect(tables.length).toBe(1);
|
|
33
|
-
db.close();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("initDb creates analytics table", () => {
|
|
37
|
-
const db = getDb(workspace);
|
|
38
|
-
initDb(db);
|
|
39
|
-
|
|
40
|
-
const tables = db.prepare(
|
|
41
|
-
"SELECT name FROM sqlite_master WHERE type='table' AND name='analytics'"
|
|
42
|
-
).all();
|
|
43
|
-
|
|
44
|
-
expect(tables.length).toBe(1);
|
|
45
|
-
db.close();
|
|
46
|
-
});
|
|
47
|
-
});
|
package/src/db.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { Database } from "bun:sqlite";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
import { existsSync } from "fs";
|
|
4
|
-
import { mkdirSync } from "fs";
|
|
5
|
-
|
|
6
|
-
const DB_FILE = "zzhub.db";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get or create SQLite database for workspace.
|
|
10
|
-
*/
|
|
11
|
-
export function getDb(workspace: string): Database {
|
|
12
|
-
if (!existsSync(workspace)) {
|
|
13
|
-
mkdirSync(workspace, { recursive: true });
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const dbPath = join(workspace, DB_FILE);
|
|
17
|
-
const db = new Database(dbPath);
|
|
18
|
-
|
|
19
|
-
// Enable WAL mode for better concurrent performance
|
|
20
|
-
db.exec("PRAGMA journal_mode = WAL;");
|
|
21
|
-
|
|
22
|
-
return db;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Initialize database schema (idempotent).
|
|
27
|
-
*/
|
|
28
|
-
export function initDb(db: Database): void {
|
|
29
|
-
db.exec(`
|
|
30
|
-
CREATE TABLE IF NOT EXISTS topics (
|
|
31
|
-
topic_id TEXT PRIMARY KEY,
|
|
32
|
-
title TEXT NOT NULL,
|
|
33
|
-
description TEXT,
|
|
34
|
-
priority TEXT CHECK(priority IN ('high', 'medium', 'low')) DEFAULT 'medium',
|
|
35
|
-
tags TEXT,
|
|
36
|
-
notes TEXT,
|
|
37
|
-
status TEXT CHECK(status IN (
|
|
38
|
-
'backlog', 'evaluating', 'scheduled', 'in_progress', 'published', 'abandoned'
|
|
39
|
-
)) DEFAULT 'backlog',
|
|
40
|
-
|
|
41
|
-
ai_score INTEGER,
|
|
42
|
-
ai_reason TEXT,
|
|
43
|
-
|
|
44
|
-
scheduled_date TEXT,
|
|
45
|
-
target_account TEXT,
|
|
46
|
-
|
|
47
|
-
run_id TEXT,
|
|
48
|
-
|
|
49
|
-
retro_performance TEXT CHECK(retro_performance IN (
|
|
50
|
-
'excellent', 'good', 'average', 'poor', null
|
|
51
|
-
)),
|
|
52
|
-
retro_lessons TEXT,
|
|
53
|
-
retro_metrics_snapshot TEXT,
|
|
54
|
-
|
|
55
|
-
created_at TEXT NOT NULL,
|
|
56
|
-
updated_at TEXT NOT NULL
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
CREATE INDEX IF NOT EXISTS idx_topics_status ON topics(status);
|
|
60
|
-
CREATE INDEX IF NOT EXISTS idx_topics_priority ON topics(priority);
|
|
61
|
-
CREATE INDEX IF NOT EXISTS idx_topics_scheduled ON topics(scheduled_date);
|
|
62
|
-
CREATE INDEX IF NOT EXISTS idx_topics_created ON topics(created_at);
|
|
63
|
-
|
|
64
|
-
CREATE TABLE IF NOT EXISTS analytics (
|
|
65
|
-
run_id TEXT PRIMARY KEY,
|
|
66
|
-
topic_id TEXT,
|
|
67
|
-
title TEXT NOT NULL,
|
|
68
|
-
publish_date TEXT NOT NULL,
|
|
69
|
-
|
|
70
|
-
reads INTEGER DEFAULT 0,
|
|
71
|
-
likes INTEGER DEFAULT 0,
|
|
72
|
-
favorites INTEGER DEFAULT 0,
|
|
73
|
-
shares INTEGER DEFAULT 0,
|
|
74
|
-
comments INTEGER DEFAULT 0,
|
|
75
|
-
|
|
76
|
-
notes TEXT,
|
|
77
|
-
recorded_at TEXT NOT NULL,
|
|
78
|
-
|
|
79
|
-
FOREIGN KEY (topic_id) REFERENCES topics(topic_id)
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
CREATE INDEX IF NOT EXISTS idx_analytics_publish_date ON analytics(publish_date);
|
|
83
|
-
CREATE INDEX IF NOT EXISTS idx_analytics_topic ON analytics(topic_id);
|
|
84
|
-
CREATE INDEX IF NOT EXISTS idx_analytics_reads ON analytics(reads);
|
|
85
|
-
`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Ensure database is initialized for workspace.
|
|
90
|
-
*/
|
|
91
|
-
export function ensureDb(workspace: string): Database {
|
|
92
|
-
const db = getDb(workspace);
|
|
93
|
-
initDb(db);
|
|
94
|
-
return db;
|
|
95
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from "bun:test";
|
|
2
|
-
import { AnalyticsSchema } from "./analytics";
|
|
3
|
-
|
|
4
|
-
describe("AnalyticsSchema", () => {
|
|
5
|
-
test("parses minimal analytics", () => {
|
|
6
|
-
const input = {
|
|
7
|
-
run_id: "run_001",
|
|
8
|
-
title: "Test Article",
|
|
9
|
-
publish_date: "2026-06-11",
|
|
10
|
-
recorded_at: "2026-06-11T10:00:00Z",
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const result = AnalyticsSchema.parse(input);
|
|
14
|
-
expect(result.run_id).toBe("run_001");
|
|
15
|
-
expect(result.reads).toBe(0);
|
|
16
|
-
expect(result.likes).toBe(0);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test("parses full analytics", () => {
|
|
20
|
-
const input = {
|
|
21
|
-
run_id: "run_002",
|
|
22
|
-
topic_id: "topic_001",
|
|
23
|
-
title: "AI Tools",
|
|
24
|
-
publish_date: "2026-06-11",
|
|
25
|
-
reads: 1500,
|
|
26
|
-
likes: 45,
|
|
27
|
-
favorites: 23,
|
|
28
|
-
shares: 12,
|
|
29
|
-
comments: 8,
|
|
30
|
-
notes: "Good performance",
|
|
31
|
-
recorded_at: "2026-06-11T10:00:00Z",
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const result = AnalyticsSchema.parse(input);
|
|
35
|
-
expect(result.reads).toBe(1500);
|
|
36
|
-
expect(result.topic_id).toBe("topic_001");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("defaults metrics to 0", () => {
|
|
40
|
-
const input = {
|
|
41
|
-
run_id: "run_003",
|
|
42
|
-
title: "Test",
|
|
43
|
-
publish_date: "2026-06-11",
|
|
44
|
-
recorded_at: "2026-06-11T10:00:00Z",
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const result = AnalyticsSchema.parse(input);
|
|
48
|
-
expect(result.reads).toBe(0);
|
|
49
|
-
expect(result.likes).toBe(0);
|
|
50
|
-
expect(result.favorites).toBe(0);
|
|
51
|
-
expect(result.shares).toBe(0);
|
|
52
|
-
expect(result.comments).toBe(0);
|
|
53
|
-
});
|
|
54
|
-
});
|
package/src/schema/analytics.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
export const AnalyticsSchema = z.object({
|
|
4
|
-
run_id: z.string(),
|
|
5
|
-
topic_id: z.string().nullable().default(null),
|
|
6
|
-
title: z.string(),
|
|
7
|
-
publish_date: z.string(),
|
|
8
|
-
|
|
9
|
-
reads: z.number().int().default(0),
|
|
10
|
-
likes: z.number().int().default(0),
|
|
11
|
-
favorites: z.number().int().default(0),
|
|
12
|
-
shares: z.number().int().default(0),
|
|
13
|
-
comments: z.number().int().default(0),
|
|
14
|
-
|
|
15
|
-
notes: z.string().nullable().default(null),
|
|
16
|
-
recorded_at: z.string(),
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
export type Analytics = z.infer<typeof AnalyticsSchema>;
|
package/src/schema/topic.test.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from "bun:test";
|
|
2
|
-
import { TopicSchema } from "./topic";
|
|
3
|
-
|
|
4
|
-
describe("TopicSchema", () => {
|
|
5
|
-
test("parses minimal topic", () => {
|
|
6
|
-
const input = {
|
|
7
|
-
topic_id: "topic_001",
|
|
8
|
-
title: "Test Topic",
|
|
9
|
-
created_at: "2026-06-11T00:00:00Z",
|
|
10
|
-
updated_at: "2026-06-11T00:00:00Z",
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const result = TopicSchema.parse(input);
|
|
14
|
-
expect(result.topic_id).toBe("topic_001");
|
|
15
|
-
expect(result.status).toBe("backlog");
|
|
16
|
-
expect(result.priority).toBe("medium");
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test("parses full topic", () => {
|
|
20
|
-
const input = {
|
|
21
|
-
topic_id: "topic_002",
|
|
22
|
-
title: "AI Tools",
|
|
23
|
-
description: "About AI tools",
|
|
24
|
-
priority: "high",
|
|
25
|
-
tags: ["AI", "tools"],
|
|
26
|
-
notes: "Some notes",
|
|
27
|
-
status: "scheduled",
|
|
28
|
-
ai_score: 85,
|
|
29
|
-
ai_reason: "Good topic",
|
|
30
|
-
scheduled_date: "2026-06-15",
|
|
31
|
-
target_account: "default",
|
|
32
|
-
run_id: "run_001",
|
|
33
|
-
retro_performance: "good",
|
|
34
|
-
retro_lessons: "Learned something",
|
|
35
|
-
retro_metrics_snapshot: { reads: 1500 },
|
|
36
|
-
created_at: "2026-06-11T00:00:00Z",
|
|
37
|
-
updated_at: "2026-06-11T00:00:00Z",
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const result = TopicSchema.parse(input);
|
|
41
|
-
expect(result.priority).toBe("high");
|
|
42
|
-
expect(result.tags).toEqual(["AI", "tools"]);
|
|
43
|
-
expect(result.ai_score).toBe(85);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test("rejects invalid priority", () => {
|
|
47
|
-
const input = {
|
|
48
|
-
topic_id: "topic_003",
|
|
49
|
-
title: "Test",
|
|
50
|
-
priority: "urgent",
|
|
51
|
-
created_at: "2026-06-11T00:00:00Z",
|
|
52
|
-
updated_at: "2026-06-11T00:00:00Z",
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
expect(() => TopicSchema.parse(input)).toThrow();
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("rejects invalid ai_score", () => {
|
|
59
|
-
const input = {
|
|
60
|
-
topic_id: "topic_004",
|
|
61
|
-
title: "Test",
|
|
62
|
-
ai_score: 150,
|
|
63
|
-
created_at: "2026-06-11T00:00:00Z",
|
|
64
|
-
updated_at: "2026-06-11T00:00:00Z",
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
expect(() => TopicSchema.parse(input)).toThrow();
|
|
68
|
-
});
|
|
69
|
-
});
|
package/src/schema/topic.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Zod schema for Topic.
|
|
3
|
-
*
|
|
4
|
-
* Topics represent content ideas tracked through the pipeline,
|
|
5
|
-
* from backlog through evaluation, scheduling, and publication.
|
|
6
|
-
* Includes AI evaluation fields and retrospective data.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { z } from "zod";
|
|
10
|
-
|
|
11
|
-
export const TopicPrioritySchema = z.enum(["high", "medium", "low"]);
|
|
12
|
-
export const TopicStatusSchema = z.enum([
|
|
13
|
-
"backlog",
|
|
14
|
-
"evaluating",
|
|
15
|
-
"scheduled",
|
|
16
|
-
"in_progress",
|
|
17
|
-
"published",
|
|
18
|
-
"abandoned",
|
|
19
|
-
]);
|
|
20
|
-
export const RetroPerformanceSchema = z.enum([
|
|
21
|
-
"excellent",
|
|
22
|
-
"good",
|
|
23
|
-
"average",
|
|
24
|
-
"poor",
|
|
25
|
-
]);
|
|
26
|
-
|
|
27
|
-
export const TopicSchema = z.object({
|
|
28
|
-
topic_id: z.string(),
|
|
29
|
-
title: z.string(),
|
|
30
|
-
description: z.string().nullable().default(null),
|
|
31
|
-
priority: TopicPrioritySchema.default("medium"),
|
|
32
|
-
tags: z.array(z.string()).default([]),
|
|
33
|
-
notes: z.string().nullable().default(null),
|
|
34
|
-
status: TopicStatusSchema.default("backlog"),
|
|
35
|
-
|
|
36
|
-
ai_score: z.number().int().min(0).max(100).nullable().default(null),
|
|
37
|
-
ai_reason: z.string().nullable().default(null),
|
|
38
|
-
|
|
39
|
-
scheduled_date: z.string().nullable().default(null),
|
|
40
|
-
target_account: z.string().nullable().default(null),
|
|
41
|
-
|
|
42
|
-
run_id: z.string().nullable().default(null),
|
|
43
|
-
|
|
44
|
-
retro_performance: RetroPerformanceSchema.nullable().default(null),
|
|
45
|
-
retro_lessons: z.string().nullable().default(null),
|
|
46
|
-
retro_metrics_snapshot: z.any().nullable().default(null),
|
|
47
|
-
|
|
48
|
-
created_at: z.string(),
|
|
49
|
-
updated_at: z.string(),
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
export type Topic = z.infer<typeof TopicSchema>;
|
|
53
|
-
export type TopicPriority = z.infer<typeof TopicPrioritySchema>;
|
|
54
|
-
export type TopicStatus = z.infer<typeof TopicStatusSchema>;
|
|
55
|
-
export type RetroPerformance = z.infer<typeof RetroPerformanceSchema>;
|