@gobing-ai/knowledge-kit 0.0.16 → 0.0.18

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.
Files changed (77) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +74 -0
  3. package/dist/index.js +46 -100
  4. package/package.json +19 -1
  5. package/plugins/generations/content-gen/dist/index.js +42 -94
  6. package/plugins/generations/core-facts-gen/dist/index.js +51 -100
  7. package/plugins/generations/daily-article-gen/dist/index.js +98 -100
  8. package/plugins/generations/daily-article-gen/src/index.ts +80 -6
  9. package/plugins/generations/dailynews-gen/dist/index.js +144 -130
  10. package/plugins/generations/dailynews-gen/src/script-builder.ts +141 -33
  11. package/plugins/generations/episode-plan-gen/dist/index.js +124 -115
  12. package/plugins/generations/episode-plan-gen/src/index.ts +111 -24
  13. package/plugins/generations/image-gen/dist/index.js +63 -115
  14. package/plugins/generations/news-report-gen/dist/index.js +164 -100
  15. package/plugins/generations/news-report-gen/src/index.ts +199 -7
  16. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/pipeline.py +17 -1
  17. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/qc.py +4 -22
  18. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/voicescript.py +8 -0
  19. package/plugins/generations/voice-gen/dist/index.js +136 -117
  20. package/plugins/generations/voice-gen/src/concat.ts +34 -0
  21. package/plugins/generations/voice-gen/src/index.ts +6 -4
  22. package/plugins/generations/voice-gen/src/voicescript.ts +61 -0
  23. package/plugins/ingestions/aihot-ingest/dist/index.js +47 -97
  24. package/plugins/ingestions/horizon-ingest/dist/index.js +47 -96
  25. package/plugins/ingestions/karakeep-local/dist/index.js +217 -266
  26. package/plugins/ingestions/last30days-ingest/dist/index.js +42 -91
  27. package/plugins/ingestions/web-search/dist/index.js +68 -117
  28. package/plugins/kk/commands/workflow-run.md +25 -2
  29. package/plugins/kk/plugin.json +1 -1
  30. package/plugins/kk/scripts/kk-workflow-stages.ts +522 -163
  31. package/plugins/kk/scripts/validate-voicescript.ts +55 -0
  32. package/plugins/kk/skills/publish/SKILL.md +80 -0
  33. package/plugins/kk/workflows/kk-daily-ai-voice.yaml +125 -120
  34. package/plugins/kk/workflows/leftover-publish-fanout-example.yaml +53 -0
  35. package/plugins/publishings/emdash-pub/dist/index.js +42 -91
  36. package/plugins/publishings/infoq-pub/dist/index.js +21994 -0
  37. package/plugins/publishings/infoq-pub/package.json +17 -0
  38. package/plugins/publishings/infoq-pub/plugin.json +7 -0
  39. package/plugins/publishings/infoq-pub/src/index.ts +123 -0
  40. package/plugins/publishings/infoq-pub/tsconfig.json +4 -0
  41. package/plugins/publishings/juejin-pub/dist/index.js +21994 -0
  42. package/plugins/publishings/juejin-pub/package.json +17 -0
  43. package/plugins/publishings/juejin-pub/plugin.json +7 -0
  44. package/plugins/publishings/juejin-pub/src/index.ts +123 -0
  45. package/plugins/publishings/juejin-pub/tsconfig.json +4 -0
  46. package/plugins/publishings/medium-pub/dist/index.js +22041 -0
  47. package/plugins/publishings/medium-pub/package.json +17 -0
  48. package/plugins/publishings/medium-pub/plugin.json +7 -0
  49. package/plugins/publishings/medium-pub/src/index.ts +215 -0
  50. package/plugins/publishings/medium-pub/tsconfig.json +4 -0
  51. package/plugins/publishings/podcast-pub/dist/index.js +183 -123
  52. package/plugins/publishings/podcast-pub/src/index.ts +47 -3
  53. package/plugins/publishings/podcast-pub/src/show-notes.ts +153 -34
  54. package/plugins/publishings/qiita-pub/dist/index.js +42 -91
  55. package/plugins/publishings/substack-pub/dist/index.js +21994 -0
  56. package/plugins/publishings/substack-pub/package.json +17 -0
  57. package/plugins/publishings/substack-pub/plugin.json +7 -0
  58. package/plugins/publishings/substack-pub/src/index.ts +123 -0
  59. package/plugins/publishings/substack-pub/tsconfig.json +4 -0
  60. package/plugins/publishings/surfdash-pub/dist/index.js +172 -103
  61. package/plugins/publishings/surfdash-pub/src/index.ts +32 -2
  62. package/plugins/publishings/wechatmp-pub/dist/index.js +22119 -0
  63. package/plugins/publishings/wechatmp-pub/package.json +17 -0
  64. package/plugins/publishings/wechatmp-pub/plugin.json +7 -0
  65. package/plugins/publishings/wechatmp-pub/src/index.ts +304 -0
  66. package/plugins/publishings/wechatmp-pub/tsconfig.json +4 -0
  67. package/plugins/publishings/x-pub/dist/index.js +22110 -0
  68. package/plugins/publishings/x-pub/package.json +17 -0
  69. package/plugins/publishings/x-pub/plugin.json +7 -0
  70. package/plugins/publishings/x-pub/src/index.ts +222 -0
  71. package/plugins/publishings/x-pub/tsconfig.json +4 -0
  72. package/plugins/publishings/xhs-pub/dist/index.js +21994 -0
  73. package/plugins/publishings/xhs-pub/package.json +17 -0
  74. package/plugins/publishings/xhs-pub/plugin.json +7 -0
  75. package/plugins/publishings/xhs-pub/src/index.ts +123 -0
  76. package/plugins/publishings/xhs-pub/tsconfig.json +4 -0
  77. package/plugins/publishings/zenn-pub/dist/index.js +42 -91
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@gobing-ai/wechatmp-pub",
3
+ "type": "module",
4
+ "private": true,
5
+ "scripts": {
6
+ "typecheck": "tsc --noEmit"
7
+ },
8
+ "dependencies": {
9
+ "@gobing-ai/kk-core": "workspace:*",
10
+ "@gobing-ai/publish-harness": "workspace:*",
11
+ "@gobing-ai/ts-runtime": "catalog:",
12
+ "@gobing-ai/ts-utils": "catalog:"
13
+ },
14
+ "devDependencies": {
15
+ "@types/bun": "1.3.14"
16
+ }
17
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "wechatmp-pub",
3
+ "kind": "publish",
4
+ "entry": "./dist/index.js",
5
+ "version": "1.0.0",
6
+ "description": "Publish plugin for WeChat Official Account drafts via official draft/add"
7
+ }
@@ -0,0 +1,304 @@
1
+ import { dirname } from 'node:path';
2
+ import { parseArgs } from 'node:util';
3
+ import { type Content, ContentSchema, type Result, ResultSchema } from '@gobing-ai/kk-core';
4
+ import { FakeFileCliTransport, type PublishTransport, type PublishTransportPayload } from '@gobing-ai/publish-harness';
5
+ import { createNodeFileSystem } from '@gobing-ai/ts-runtime';
6
+ import { echoError } from '@gobing-ai/ts-utils';
7
+
8
+ const TARGET = 'wechatmp';
9
+ const TOKEN_URL = 'https://api.weixin.qq.com/cgi-bin/token';
10
+ const DRAFT_ADD_URL = 'https://api.weixin.qq.com/cgi-bin/draft/add';
11
+ const APP_ID_ENV = 'WECHAT_APP_ID';
12
+ const APP_SECRET_ENV = 'WECHAT_APP_SECRET';
13
+
14
+ export interface WechatDraftArticle {
15
+ article_type: 'news';
16
+ title: string;
17
+ author?: string;
18
+ digest?: string;
19
+ content: string;
20
+ content_source_url?: string;
21
+ thumb_media_id: string;
22
+ need_open_comment?: number;
23
+ only_fans_can_comment?: number;
24
+ }
25
+
26
+ export interface WechatDraftArticles {
27
+ articles: WechatDraftArticle[];
28
+ }
29
+
30
+ function fail(error: string): Result {
31
+ return ResultSchema.parse({ ok: false, target: TARGET, error });
32
+ }
33
+
34
+ function escapeHtml(text: string): string {
35
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
36
+ }
37
+
38
+ /**
39
+ * Harvested subset of wt md-to-wechat: headings, paragraphs, fenced code, lists, inline.
40
+ * TypeScript only — no Python, no theme CLI.
41
+ */
42
+ export function markdownToWechatHtml(markdown: string): string {
43
+ const lines = markdown.replace(/\r\n/g, '\n').split('\n');
44
+ const out: string[] = [];
45
+ let inCode = false;
46
+ let code: string[] = [];
47
+ let listType: 'ul' | 'ol' | null = null;
48
+
49
+ const closeList = (): void => {
50
+ if (listType) {
51
+ out.push(`</${listType}>`);
52
+ listType = null;
53
+ }
54
+ };
55
+
56
+ const inline = (text: string): string =>
57
+ escapeHtml(text)
58
+ .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
59
+ .replace(/\*([^*]+)\*/g, '<em>$1</em>')
60
+ .replace(/`([^`]+)`/g, '<code>$1</code>')
61
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
62
+
63
+ for (const line of lines) {
64
+ if (line.startsWith('```')) {
65
+ if (inCode) {
66
+ out.push(`<pre><code>${escapeHtml(code.join('\n'))}</code></pre>`);
67
+ code = [];
68
+ inCode = false;
69
+ } else {
70
+ closeList();
71
+ inCode = true;
72
+ }
73
+ continue;
74
+ }
75
+ if (inCode) {
76
+ code.push(line);
77
+ continue;
78
+ }
79
+ const heading = line.match(/^(#{1,6})\s+(.+)$/);
80
+ if (heading?.[1] && heading[2]) {
81
+ closeList();
82
+ const level = Math.min(heading[1].length, 3);
83
+ out.push(`<h${level}>${inline(heading[2])}</h${level}>`);
84
+ continue;
85
+ }
86
+ const ul = line.match(/^[-*]\s+(.+)$/);
87
+ if (ul?.[1]) {
88
+ if (listType !== 'ul') {
89
+ closeList();
90
+ listType = 'ul';
91
+ out.push('<ul>');
92
+ }
93
+ out.push(`<li>${inline(ul[1])}</li>`);
94
+ continue;
95
+ }
96
+ const ol = line.match(/^\d+\.\s+(.+)$/);
97
+ if (ol?.[1]) {
98
+ if (listType !== 'ol') {
99
+ closeList();
100
+ listType = 'ol';
101
+ out.push('<ol>');
102
+ }
103
+ out.push(`<li>${inline(ol[1])}</li>`);
104
+ continue;
105
+ }
106
+ closeList();
107
+ if (line.trim().length === 0) continue;
108
+ out.push(`<p>${inline(line.trim())}</p>`);
109
+ }
110
+ closeList();
111
+ if (inCode) out.push(`<pre><code>${escapeHtml(code.join('\n'))}</code></pre>`);
112
+ return out.join('');
113
+ }
114
+
115
+ function resolveTitle(content: Content): string {
116
+ if (content.title && content.title.trim().length > 0) return content.title.trim();
117
+ const match = content.body.match(/^#\s+(.+)$/m);
118
+ return match?.[1] ? match[1].trim() : 'Untitled';
119
+ }
120
+
121
+ function resolveDigest(content: Content): string {
122
+ if (typeof content.metadata?.description === 'string' && content.metadata.description.trim().length > 0) {
123
+ return content.metadata.description.trim().slice(0, 120);
124
+ }
125
+ const para = content.body
126
+ .replace(/^#.+$/m, '')
127
+ .split(/\n\n+/)
128
+ .map((p) => p.trim())
129
+ .find((p) => p.length > 0);
130
+ return (para ?? '').slice(0, 120);
131
+ }
132
+
133
+ export function mapContentToWechatDraft(content: Content): WechatDraftArticles {
134
+ const thumb = content.metadata?.thumbMediaId;
135
+ if (typeof thumb !== 'string' || thumb.trim().length === 0) {
136
+ throw new Error(
137
+ 'metadata.thumbMediaId is required for WeChat news drafts (cover media_id from material/add_material)',
138
+ );
139
+ }
140
+ const title = resolveTitle(content).slice(0, 32);
141
+ const author = typeof content.metadata?.author === 'string' ? content.metadata.author.slice(0, 16) : undefined;
142
+ const source =
143
+ typeof content.metadata?.canonicalUrl === 'string'
144
+ ? content.metadata.canonicalUrl
145
+ : content.references?.[0]?.url;
146
+ return {
147
+ articles: [
148
+ {
149
+ article_type: 'news',
150
+ title,
151
+ ...(author ? { author } : {}),
152
+ digest: resolveDigest(content),
153
+ content: markdownToWechatHtml(content.body),
154
+ ...(source ? { content_source_url: source } : {}),
155
+ thumb_media_id: thumb.trim(),
156
+ },
157
+ ],
158
+ };
159
+ }
160
+
161
+ function resolveOperation(options?: Record<string, unknown>, content?: Content): string {
162
+ const raw = options?.operation ?? content?.metadata?.operation ?? 'draft';
163
+ return typeof raw === 'string' ? raw : 'draft';
164
+ }
165
+
166
+ export class WechatDraftApiTransport implements PublishTransport {
167
+ public readonly kind = 'file-cli';
168
+
169
+ public async publish(payload: PublishTransportPayload): Promise<Result> {
170
+ const operation = resolveOperation(payload.options, payload.content);
171
+ if (operation === 'live' || operation === 'publish' || operation === 'freepublish') {
172
+ return fail(
173
+ 'WeChat live publish via freepublish/submit is certification-gated; default operation is draft. Confirm with the operator before a live/console path.',
174
+ );
175
+ }
176
+ if (operation !== 'draft') {
177
+ return fail(`invalid options.operation "${operation}": expected draft (default) or live`);
178
+ }
179
+
180
+ const appId = process.env[APP_ID_ENV];
181
+ const appSecret = process.env[APP_SECRET_ENV];
182
+ const missing: string[] = [];
183
+ if (!appId || appId.trim().length === 0) missing.push(APP_ID_ENV);
184
+ if (!appSecret || appSecret.trim().length === 0) missing.push(APP_SECRET_ENV);
185
+ if (missing.length > 0) {
186
+ return fail(
187
+ `${missing.join(' and ')} environment variable${missing.length > 1 ? 's are' : ' is'} required: WeChat Official Account AppID/AppSecret`,
188
+ );
189
+ }
190
+
191
+ let articles: WechatDraftArticles;
192
+ try {
193
+ articles = mapContentToWechatDraft(payload.content);
194
+ } catch (error: unknown) {
195
+ return fail(error instanceof Error ? error.message : String(error));
196
+ }
197
+
198
+ try {
199
+ const tokenUrl = `${TOKEN_URL}?grant_type=client_credential&appid=${encodeURIComponent(appId ?? '')}&secret=${encodeURIComponent(appSecret ?? '')}`;
200
+ const tokenResponse = await fetch(tokenUrl);
201
+ const tokenJson = (await tokenResponse.json()) as {
202
+ access_token?: unknown;
203
+ errmsg?: unknown;
204
+ errcode?: unknown;
205
+ };
206
+ if (typeof tokenJson.access_token !== 'string') {
207
+ return fail(
208
+ `WeChat token request failed: ${typeof tokenJson.errmsg === 'string' ? tokenJson.errmsg : 'missing access_token'}`,
209
+ );
210
+ }
211
+ const draftResponse = await fetch(
212
+ `${DRAFT_ADD_URL}?access_token=${encodeURIComponent(tokenJson.access_token)}`,
213
+ {
214
+ method: 'POST',
215
+ headers: { 'Content-Type': 'application/json' },
216
+ body: JSON.stringify(articles),
217
+ },
218
+ );
219
+ const draftJson = (await draftResponse.json()) as {
220
+ media_id?: unknown;
221
+ errmsg?: unknown;
222
+ errcode?: unknown;
223
+ };
224
+ if (typeof draftJson.media_id !== 'string') {
225
+ return fail(
226
+ `WeChat draft/add failed: ${typeof draftJson.errmsg === 'string' ? draftJson.errmsg : 'missing media_id'}`,
227
+ );
228
+ }
229
+ return ResultSchema.parse({
230
+ ok: true,
231
+ target: TARGET,
232
+ id: draftJson.media_id,
233
+ message: 'WeChat draft created via draft/add',
234
+ metadata: { operation: 'draft', publishStatus: 'draft' },
235
+ });
236
+ } catch (err: unknown) {
237
+ return fail(`WeChat transport error: ${err instanceof Error ? err.message : String(err)}`);
238
+ }
239
+ }
240
+ }
241
+
242
+ export function getPublishTransport(): PublishTransport {
243
+ if (process.env.KNOWLEDGE_KIT_PUBLISH_TRANSPORT === 'fake') {
244
+ return new FakeFileCliTransport();
245
+ }
246
+ return new WechatDraftApiTransport();
247
+ }
248
+
249
+ async function writePublishResult(outputPath: string, result: Result): Promise<void> {
250
+ const outDir = dirname(outputPath);
251
+ if (outDir && outDir !== '.') {
252
+ await createNodeFileSystem().ensureDir(outDir);
253
+ }
254
+ await createNodeFileSystem().writeFile(outputPath, JSON.stringify(result, null, 2));
255
+ }
256
+
257
+ export async function processPublishIO(
258
+ inputPath: string,
259
+ outputPath: string,
260
+ transport: PublishTransport = getPublishTransport(),
261
+ ): Promise<void> {
262
+ await createNodeFileSystem().deleteFile(outputPath);
263
+ let content: Content;
264
+ let options: Record<string, unknown> | undefined;
265
+ try {
266
+ const raw = JSON.parse(await createNodeFileSystem().readFile(inputPath)) as Record<string, unknown>;
267
+ content = ContentSchema.parse(raw);
268
+ if (raw.options !== undefined && typeof raw.options === 'object' && raw.options !== null) {
269
+ options = raw.options as Record<string, unknown>;
270
+ }
271
+ } catch (error: unknown) {
272
+ const message = `Invalid Content input: ${error instanceof Error ? error.message : String(error)}`;
273
+ await writePublishResult(outputPath, fail(message));
274
+ throw new Error(message);
275
+ }
276
+ const result = ResultSchema.parse(await transport.publish({ content, options }));
277
+ await writePublishResult(outputPath, result);
278
+ if (!result.ok) throw new Error(result.error ?? 'Publish failed');
279
+ }
280
+
281
+ export async function main(): Promise<number> {
282
+ let values: { in?: string; out?: string };
283
+ try {
284
+ ({ values } = parseArgs({ options: { in: { type: 'string' }, out: { type: 'string' } } }));
285
+ } catch (error: unknown) {
286
+ echoError(`Invalid arguments: ${error instanceof Error ? error.message : String(error)}`);
287
+ return 1;
288
+ }
289
+ if (!values.in || !values.out) {
290
+ echoError('Missing required arguments: --in and --out');
291
+ return 1;
292
+ }
293
+ try {
294
+ await processPublishIO(values.in, values.out);
295
+ return 0;
296
+ } catch (error: unknown) {
297
+ echoError(`wechatmp-pub failed: ${error instanceof Error ? error.message : String(error)}`);
298
+ return 1;
299
+ }
300
+ }
301
+
302
+ if (import.meta.main) {
303
+ process.exit(await main());
304
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "../../../tooling/typescript/base.json",
3
+ "include": ["src", "tests"]
4
+ }