@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +74 -0
- package/dist/index.js +46 -100
- package/package.json +19 -1
- package/plugins/generations/content-gen/dist/index.js +42 -94
- package/plugins/generations/core-facts-gen/dist/index.js +51 -100
- package/plugins/generations/daily-article-gen/dist/index.js +98 -100
- package/plugins/generations/daily-article-gen/src/index.ts +80 -6
- package/plugins/generations/dailynews-gen/dist/index.js +144 -130
- package/plugins/generations/dailynews-gen/src/script-builder.ts +141 -33
- package/plugins/generations/episode-plan-gen/dist/index.js +124 -115
- package/plugins/generations/episode-plan-gen/src/index.ts +111 -24
- package/plugins/generations/image-gen/dist/index.js +63 -115
- package/plugins/generations/news-report-gen/dist/index.js +164 -100
- package/plugins/generations/news-report-gen/src/index.ts +199 -7
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/pipeline.py +17 -1
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/qc.py +4 -22
- package/plugins/generations/omni-voice-gen/src/omni_voice_gen/voicescript.py +8 -0
- package/plugins/generations/voice-gen/dist/index.js +136 -117
- package/plugins/generations/voice-gen/src/concat.ts +34 -0
- package/plugins/generations/voice-gen/src/index.ts +6 -4
- package/plugins/generations/voice-gen/src/voicescript.ts +61 -0
- package/plugins/ingestions/aihot-ingest/dist/index.js +47 -97
- package/plugins/ingestions/horizon-ingest/dist/index.js +47 -96
- package/plugins/ingestions/karakeep-local/dist/index.js +217 -266
- package/plugins/ingestions/last30days-ingest/dist/index.js +42 -91
- package/plugins/ingestions/web-search/dist/index.js +68 -117
- package/plugins/kk/commands/workflow-run.md +25 -2
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/kk-workflow-stages.ts +522 -163
- package/plugins/kk/scripts/validate-voicescript.ts +55 -0
- package/plugins/kk/skills/publish/SKILL.md +80 -0
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +125 -120
- package/plugins/kk/workflows/leftover-publish-fanout-example.yaml +53 -0
- package/plugins/publishings/emdash-pub/dist/index.js +42 -91
- package/plugins/publishings/infoq-pub/dist/index.js +21994 -0
- package/plugins/publishings/infoq-pub/package.json +17 -0
- package/plugins/publishings/infoq-pub/plugin.json +7 -0
- package/plugins/publishings/infoq-pub/src/index.ts +123 -0
- package/plugins/publishings/infoq-pub/tsconfig.json +4 -0
- package/plugins/publishings/juejin-pub/dist/index.js +21994 -0
- package/plugins/publishings/juejin-pub/package.json +17 -0
- package/plugins/publishings/juejin-pub/plugin.json +7 -0
- package/plugins/publishings/juejin-pub/src/index.ts +123 -0
- package/plugins/publishings/juejin-pub/tsconfig.json +4 -0
- package/plugins/publishings/medium-pub/dist/index.js +22041 -0
- package/plugins/publishings/medium-pub/package.json +17 -0
- package/plugins/publishings/medium-pub/plugin.json +7 -0
- package/plugins/publishings/medium-pub/src/index.ts +215 -0
- package/plugins/publishings/medium-pub/tsconfig.json +4 -0
- package/plugins/publishings/podcast-pub/dist/index.js +183 -123
- package/plugins/publishings/podcast-pub/src/index.ts +47 -3
- package/plugins/publishings/podcast-pub/src/show-notes.ts +153 -34
- package/plugins/publishings/qiita-pub/dist/index.js +42 -91
- package/plugins/publishings/substack-pub/dist/index.js +21994 -0
- package/plugins/publishings/substack-pub/package.json +17 -0
- package/plugins/publishings/substack-pub/plugin.json +7 -0
- package/plugins/publishings/substack-pub/src/index.ts +123 -0
- package/plugins/publishings/substack-pub/tsconfig.json +4 -0
- package/plugins/publishings/surfdash-pub/dist/index.js +172 -103
- package/plugins/publishings/surfdash-pub/src/index.ts +32 -2
- package/plugins/publishings/wechatmp-pub/dist/index.js +22119 -0
- package/plugins/publishings/wechatmp-pub/package.json +17 -0
- package/plugins/publishings/wechatmp-pub/plugin.json +7 -0
- package/plugins/publishings/wechatmp-pub/src/index.ts +304 -0
- package/plugins/publishings/wechatmp-pub/tsconfig.json +4 -0
- package/plugins/publishings/x-pub/dist/index.js +22110 -0
- package/plugins/publishings/x-pub/package.json +17 -0
- package/plugins/publishings/x-pub/plugin.json +7 -0
- package/plugins/publishings/x-pub/src/index.ts +222 -0
- package/plugins/publishings/x-pub/tsconfig.json +4 -0
- package/plugins/publishings/xhs-pub/dist/index.js +21994 -0
- package/plugins/publishings/xhs-pub/package.json +17 -0
- package/plugins/publishings/xhs-pub/plugin.json +7 -0
- package/plugins/publishings/xhs-pub/src/index.ts +123 -0
- package/plugins/publishings/xhs-pub/tsconfig.json +4 -0
- package/plugins/publishings/zenn-pub/dist/index.js +42 -91
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gobing-ai/medium-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,215 @@
|
|
|
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
|
+
/** Medium REST API v1 (integration tokens still post; official docs archived 2023). */
|
|
9
|
+
const MEDIUM_API_BASE = 'https://api.medium.com/v1';
|
|
10
|
+
const TARGET = 'medium';
|
|
11
|
+
const TOKEN_ENV = 'MEDIUM_INTEGRATION_TOKEN';
|
|
12
|
+
const STATUS_ENV = 'MEDIUM_PUBLISH_STATUS';
|
|
13
|
+
|
|
14
|
+
export type MediumPublishStatus = 'draft' | 'unlisted' | 'public';
|
|
15
|
+
|
|
16
|
+
/** Body posted to `POST /users/{userId}/posts`. */
|
|
17
|
+
export interface MediumPostBody {
|
|
18
|
+
title: string;
|
|
19
|
+
contentFormat: 'markdown' | 'html';
|
|
20
|
+
content: string;
|
|
21
|
+
tags: string[];
|
|
22
|
+
publishStatus: MediumPublishStatus;
|
|
23
|
+
canonicalUrl?: string;
|
|
24
|
+
license?: string;
|
|
25
|
+
notifyFollowers: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function fail(error: string): Result {
|
|
29
|
+
return ResultSchema.parse({ ok: false, target: TARGET, error });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function resolveTitle(content: Content): string {
|
|
33
|
+
if (content.title && content.title.trim().length > 0) return content.title.trim();
|
|
34
|
+
const match = content.body.match(/^#\s+(.+)$/m);
|
|
35
|
+
return match?.[1] ? match[1].trim() : 'Untitled Post';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveTags(content: Content): string[] {
|
|
39
|
+
const raw: unknown[] = Array.isArray(content.metadata?.tags)
|
|
40
|
+
? content.metadata.tags
|
|
41
|
+
: Array.isArray(content.metadata?.keywords)
|
|
42
|
+
? content.metadata.keywords
|
|
43
|
+
: [];
|
|
44
|
+
const tags = raw.filter((t): t is string => typeof t === 'string' && t.trim().length > 0).map((t) => t.trim());
|
|
45
|
+
return [...new Set(tags)].slice(0, 5);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolvePublishStatus(content: Content, options?: Record<string, unknown>): MediumPublishStatus {
|
|
49
|
+
const raw = options?.publishStatus ?? content.metadata?.publishStatus ?? process.env[STATUS_ENV];
|
|
50
|
+
if (raw === 'public' || raw === 'unlisted' || raw === 'draft') return raw;
|
|
51
|
+
return 'draft';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Maps Content into the Medium create-post envelope. Default publishStatus is draft
|
|
56
|
+
* unless the operator confirms live (`public` / `unlisted`).
|
|
57
|
+
*/
|
|
58
|
+
export function mapContentToMediumPost(content: Content, options?: Record<string, unknown>): MediumPostBody {
|
|
59
|
+
const canonical =
|
|
60
|
+
typeof content.metadata?.canonicalUrl === 'string'
|
|
61
|
+
? content.metadata.canonicalUrl
|
|
62
|
+
: content.references?.[0]?.url;
|
|
63
|
+
const license = typeof content.metadata?.license === 'string' ? content.metadata.license : undefined;
|
|
64
|
+
return {
|
|
65
|
+
title: resolveTitle(content),
|
|
66
|
+
contentFormat: content.format === 'html' ? 'html' : 'markdown',
|
|
67
|
+
content: content.body,
|
|
68
|
+
tags: resolveTags(content),
|
|
69
|
+
publishStatus: resolvePublishStatus(content, options),
|
|
70
|
+
...(canonical ? { canonicalUrl: canonical } : {}),
|
|
71
|
+
...(license ? { license } : {}),
|
|
72
|
+
notifyFollowers: false,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Token-API transport: GET /me then POST /users/{id}/posts with Bearer integration token.
|
|
78
|
+
*/
|
|
79
|
+
export class MediumApiTransport implements PublishTransport {
|
|
80
|
+
public readonly kind = 'file-cli';
|
|
81
|
+
|
|
82
|
+
public async publish(payload: PublishTransportPayload): Promise<Result> {
|
|
83
|
+
const token = process.env[TOKEN_ENV];
|
|
84
|
+
if (!token || token.trim().length === 0) {
|
|
85
|
+
return fail(
|
|
86
|
+
`${TOKEN_ENV} environment variable is required: Medium self-issued integration token (https://medium.com/me/settings)`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const body = mapContentToMediumPost(payload.content, payload.options);
|
|
91
|
+
try {
|
|
92
|
+
const meResponse = await fetch(`${MEDIUM_API_BASE}/me`, {
|
|
93
|
+
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
|
|
94
|
+
});
|
|
95
|
+
if (!meResponse.ok) {
|
|
96
|
+
const errorText = (await meResponse.text()).slice(0, 1000);
|
|
97
|
+
return fail(
|
|
98
|
+
`Medium API /me failed (HTTP ${meResponse.status} ${meResponse.statusText}): ${errorText.trim() || 'Unknown error'}`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
const meJson = (await meResponse.json()) as { data?: { id?: unknown } };
|
|
102
|
+
if (typeof meJson.data?.id !== 'string') {
|
|
103
|
+
return fail('Medium API /me response missing data.id');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const postResponse = await fetch(`${MEDIUM_API_BASE}/users/${meJson.data.id}/posts`, {
|
|
107
|
+
method: 'POST',
|
|
108
|
+
headers: {
|
|
109
|
+
Authorization: `Bearer ${token}`,
|
|
110
|
+
'Content-Type': 'application/json',
|
|
111
|
+
Accept: 'application/json',
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify(body),
|
|
114
|
+
});
|
|
115
|
+
if (!postResponse.ok) {
|
|
116
|
+
const errorText = (await postResponse.text()).slice(0, 1000);
|
|
117
|
+
return fail(
|
|
118
|
+
`Medium API create-post failed (HTTP ${postResponse.status} ${postResponse.statusText}): ${errorText.trim() || 'Unknown error'}`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
const postJson = (await postResponse.json()) as {
|
|
122
|
+
data?: { id?: unknown; url?: unknown; publishStatus?: unknown };
|
|
123
|
+
};
|
|
124
|
+
if (typeof postJson.data?.id !== 'string') {
|
|
125
|
+
return fail('Medium API create-post response missing data.id');
|
|
126
|
+
}
|
|
127
|
+
const url = typeof postJson.data.url === 'string' ? postJson.data.url : undefined;
|
|
128
|
+
return ResultSchema.parse({
|
|
129
|
+
ok: true,
|
|
130
|
+
target: TARGET,
|
|
131
|
+
id: postJson.data.id,
|
|
132
|
+
url,
|
|
133
|
+
message: `Medium post created as ${body.publishStatus}`,
|
|
134
|
+
metadata: { publishStatus: body.publishStatus },
|
|
135
|
+
});
|
|
136
|
+
} catch (err: unknown) {
|
|
137
|
+
return fail(`Medium transport error: ${err instanceof Error ? err.message : String(err)}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function getPublishTransport(): PublishTransport {
|
|
143
|
+
if (process.env.KNOWLEDGE_KIT_PUBLISH_TRANSPORT === 'fake') {
|
|
144
|
+
return new FakeFileCliTransport();
|
|
145
|
+
}
|
|
146
|
+
return new MediumApiTransport();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function writePublishResult(outputPath: string, result: Result): Promise<void> {
|
|
150
|
+
const outDir = dirname(outputPath);
|
|
151
|
+
if (outDir && outDir !== '.') {
|
|
152
|
+
await createNodeFileSystem().ensureDir(outDir);
|
|
153
|
+
}
|
|
154
|
+
await createNodeFileSystem().writeFile(outputPath, JSON.stringify(result, null, 2));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function processPublishIO(
|
|
158
|
+
inputPath: string,
|
|
159
|
+
outputPath: string,
|
|
160
|
+
transport: PublishTransport = getPublishTransport(),
|
|
161
|
+
): Promise<void> {
|
|
162
|
+
await createNodeFileSystem().deleteFile(outputPath);
|
|
163
|
+
|
|
164
|
+
let content: Content;
|
|
165
|
+
let options: Record<string, unknown> | undefined;
|
|
166
|
+
try {
|
|
167
|
+
const rawInput = await createNodeFileSystem().readFile(inputPath);
|
|
168
|
+
const raw = JSON.parse(rawInput) as Record<string, unknown>;
|
|
169
|
+
content = ContentSchema.parse(raw);
|
|
170
|
+
if (raw.options !== undefined && typeof raw.options === 'object' && raw.options !== null) {
|
|
171
|
+
options = raw.options as Record<string, unknown>;
|
|
172
|
+
}
|
|
173
|
+
} catch (error: unknown) {
|
|
174
|
+
const message = `Invalid Content input: ${error instanceof Error ? error.message : String(error)}`;
|
|
175
|
+
await writePublishResult(outputPath, fail(message));
|
|
176
|
+
throw new Error(message);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const result = await transport.publish({ content, options });
|
|
180
|
+
const validatedResult = ResultSchema.parse(result);
|
|
181
|
+
await writePublishResult(outputPath, validatedResult);
|
|
182
|
+
if (!validatedResult.ok) {
|
|
183
|
+
throw new Error(validatedResult.error ?? 'Publish failed');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function main(): Promise<number> {
|
|
188
|
+
let values: { in?: string; out?: string };
|
|
189
|
+
try {
|
|
190
|
+
({ values } = parseArgs({
|
|
191
|
+
options: {
|
|
192
|
+
in: { type: 'string' },
|
|
193
|
+
out: { type: 'string' },
|
|
194
|
+
},
|
|
195
|
+
}));
|
|
196
|
+
} catch (error: unknown) {
|
|
197
|
+
echoError(`Invalid arguments: ${error instanceof Error ? error.message : String(error)}`);
|
|
198
|
+
return 1;
|
|
199
|
+
}
|
|
200
|
+
if (!values.in || !values.out) {
|
|
201
|
+
echoError('Missing required arguments: --in and --out');
|
|
202
|
+
return 1;
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
await processPublishIO(values.in, values.out);
|
|
206
|
+
return 0;
|
|
207
|
+
} catch (error: unknown) {
|
|
208
|
+
echoError(`medium-pub failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
209
|
+
return 1;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (import.meta.main) {
|
|
214
|
+
process.exit(await main());
|
|
215
|
+
}
|