@gobing-ai/knowledge-kit 0.0.30 → 0.0.32

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 (66) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/index.js +10 -10
  3. package/package.json +4 -4
  4. package/plugins/generations/content-gen/dist/index.js +9 -9
  5. package/plugins/generations/core-facts-gen/dist/index.js +12 -12
  6. package/plugins/generations/daily-article-gen/dist/index.js +12 -12
  7. package/plugins/generations/dailynews-gen/dist/index.js +10 -10
  8. package/plugins/generations/dailynews-gen/src/script-builder.ts +4 -2
  9. package/plugins/generations/episode-plan-gen/dist/index.js +23 -13
  10. package/plugins/generations/episode-plan-gen/src/index.ts +23 -2
  11. package/plugins/generations/gemini-tts/dist/index.js +9 -9
  12. package/plugins/generations/image-gen/dist/index.js +9 -9
  13. package/plugins/generations/news-report-gen/dist/index.js +12 -12
  14. package/plugins/generations/voice-gen/dist/index.js +9 -9
  15. package/plugins/ingestions/aihot-ingest/dist/index.js +12 -12
  16. package/plugins/ingestions/horizon-ingest/dist/index.js +12 -12
  17. package/plugins/ingestions/karakeep-local/dist/index.js +13 -13
  18. package/plugins/ingestions/last30days-ingest/dist/index.js +12 -12
  19. package/plugins/ingestions/web-search/dist/index.js +10 -10
  20. package/plugins/kk/plugin.json +1 -1
  21. package/plugins/kk/scripts/kk-workflow-stages.ts +173 -15
  22. package/plugins/kk/skills/content-judge/SKILL.md +16 -4
  23. package/plugins/kk/skills/content-judge/references/rubrics.md +8 -2
  24. package/plugins/kk/skills/content-judge/references/workflow-integration.md +2 -1
  25. package/plugins/kk/skills/content-safety/SKILL.md +24 -13
  26. package/plugins/kk/workflows/kk-daily-ai-voice.yaml +65 -8
  27. package/plugins/publishings/emdash-pub/dist/index.js +9 -9
  28. package/plugins/publishings/infoq-pub/dist/index.js +8602 -211
  29. package/plugins/publishings/infoq-pub/package.json +3 -1
  30. package/plugins/publishings/infoq-pub/plugin.json +1 -1
  31. package/plugins/publishings/infoq-pub/src/index.ts +246 -29
  32. package/plugins/publishings/infoq-pub/src/profile-check.ts +19 -0
  33. package/plugins/publishings/juejin-pub/dist/index.js +8665 -211
  34. package/plugins/publishings/juejin-pub/package.json +3 -1
  35. package/plugins/publishings/juejin-pub/plugin.json +1 -1
  36. package/plugins/publishings/juejin-pub/src/index.ts +305 -29
  37. package/plugins/publishings/juejin-pub/src/profile-check.ts +19 -0
  38. package/plugins/publishings/medium-pub/dist/index.js +9 -9
  39. package/plugins/publishings/podcast-pub/dist/index.js +9 -9
  40. package/plugins/publishings/qiita-pub/dist/index.js +9 -9
  41. package/plugins/publishings/substack-pub/dist/index.js +8610 -211
  42. package/plugins/publishings/substack-pub/package.json +3 -1
  43. package/plugins/publishings/substack-pub/plugin.json +1 -1
  44. package/plugins/publishings/substack-pub/src/index.ts +271 -29
  45. package/plugins/publishings/substack-pub/src/profile-check.ts +74 -0
  46. package/plugins/publishings/surfdash-pub/dist/index.js +9 -9
  47. package/plugins/publishings/wechatmp-pub/dist/index.js +1227 -348
  48. package/plugins/publishings/wechatmp-pub/src/enrich.ts +229 -0
  49. package/plugins/publishings/wechatmp-pub/src/flow.ts +896 -0
  50. package/plugins/publishings/wechatmp-pub/src/index.ts +186 -516
  51. package/plugins/publishings/wechatmp-pub/src/modes.ts +403 -0
  52. package/plugins/publishings/wechatmp-pub/src/page.ts +260 -0
  53. package/plugins/publishings/wechatmp-pub/src/profile-check.ts +7 -5
  54. package/plugins/publishings/x-pub/dist/index.js +8626 -221
  55. package/plugins/publishings/x-pub/package.json +3 -1
  56. package/plugins/publishings/x-pub/plugin.json +1 -1
  57. package/plugins/publishings/x-pub/src/index.ts +258 -20
  58. package/plugins/publishings/x-pub/src/profile-check.ts +19 -0
  59. package/plugins/publishings/xhs-pub/dist/index.js +843 -346
  60. package/plugins/publishings/xhs-pub/plugin.json +1 -1
  61. package/plugins/publishings/xhs-pub/src/flow.ts +572 -0
  62. package/plugins/publishings/xhs-pub/src/index.ts +101 -593
  63. package/plugins/publishings/xhs-pub/src/modes.ts +300 -0
  64. package/plugins/publishings/xhs-pub/src/page.ts +267 -0
  65. package/plugins/publishings/xhs-pub/src/profile-check.ts +2 -3
  66. package/plugins/publishings/zenn-pub/dist/index.js +9 -9
@@ -7,9 +7,11 @@
7
7
  },
8
8
  "dependencies": {
9
9
  "@gobing-ai/kk-core": "workspace:*",
10
+ "@gobing-ai/ts-browser-automation": "catalog:",
10
11
  "@gobing-ai/publish-harness": "workspace:*",
11
12
  "@gobing-ai/ts-runtime": "catalog:",
12
- "@gobing-ai/ts-utils": "catalog:"
13
+ "@gobing-ai/ts-utils": "catalog:",
14
+ "playwright": "catalog:"
13
15
  },
14
16
  "devDependencies": {
15
17
  "@types/bun": "1.3.14"
@@ -3,5 +3,5 @@
3
3
  "kind": "publish",
4
4
  "entry": "./dist/index.js",
5
5
  "version": "1.0.0",
6
- "description": "Publish plugin for Juejin (juejin.cn) via browser/agent transport"
6
+ "description": "Publish plugin for Juejin (juejin.cn): article drafts through the bytemd editor via script browser transport (ts-browser-automation)"
7
7
  }
@@ -6,63 +6,339 @@ import {
6
6
  FakeBrowserTransport,
7
7
  type PublishTransport,
8
8
  type PublishTransportPayload,
9
- resolveBrowserProfileDir,
9
+ trySelectors,
10
10
  } from '@gobing-ai/publish-harness';
11
+ import {
12
+ type ChannelBrowserConfig,
13
+ checkChannelSession,
14
+ expectOutcome,
15
+ fillCodeMirror,
16
+ readScopedSignal,
17
+ resolveChannelProfileDir,
18
+ resolveSelector,
19
+ waitUntil,
20
+ } from '@gobing-ai/publish-harness/browser';
21
+ import {
22
+ type BrowserLauncher,
23
+ BrowserProfileMissingError,
24
+ type IsAuthenticatedPredicate,
25
+ LoginTimeoutError,
26
+ withProfilePage,
27
+ } from '@gobing-ai/ts-browser-automation';
11
28
  import { createNodeFileSystem } from '@gobing-ai/ts-runtime';
12
29
  import { echoError } from '@gobing-ai/ts-utils';
30
+ import type { Page } from 'playwright';
13
31
 
14
- const CHANNEL = 'juejin';
32
+ /** Channel slug: names the Result target and resolves the `JUEJIN_PROFILE_DIR` tier. */
33
+ export const CHANNEL = 'juejin';
15
34
  const TARGET = 'juejin';
35
+ /** Juejin's new-draft editor — the seeded URL the operator dogfood re-measures (R1). */
36
+ export const EDITOR_URL = 'https://juejin.cn/editor/drafts/new?v=2';
37
+ /** Login/SSO URLs that mean "not authenticated" — the editor never renders behind them. */
38
+ export const LOGIN_URL_RE = /(\/login|passport\.juejin\.cn)/;
39
+
40
+ /**
41
+ * Selectors measured against the Juejin editor (wt seed `juejin-playwright.ts:26-62`); each list is
42
+ * tried in order and the operator dogfood re-measures them. `publish` opens the live popup and
43
+ * `confirm` publishes it — both live-only (E91-D5).
44
+ */
45
+ export const SELECTORS = {
46
+ title: ['input.title-input', 'input[placeholder*="标题"]'],
47
+ body: ['.bytemd-editor .CodeMirror', '.CodeMirror', '.cm-editor'],
48
+ publish: ['button:has-text("发布")'],
49
+ category: '.category-list .item',
50
+ tagInput: '.tag-input input',
51
+ confirm: ['button:has-text("确定并发布")'],
52
+ } as const;
53
+
54
+ const STEP_TIMEOUT_MS = 30_000;
55
+ const LOGIN_TIMEOUT_MS = 180_000;
56
+ /** Juejin autosaves the draft on input: the design's window before the one manual save key press. */
57
+ const AUTOSAVE_TIMEOUT_MS = 5_000;
58
+ const SAVE_KEY = process.platform === 'darwin' ? 'Meta+s' : 'Control+s';
59
+ /** Operator recovery command every session failure names — never a cookie, page HTML or stack. */
60
+ const LOGIN_INSTRUCTION = 'bun plugins/publishings/juejin-pub/src/profile-check.ts login';
61
+ /** A saved draft lands on `/editor/drafts/<id>`; a live publish on `/post/<id>` or `/published/<id>`. */
62
+ const DRAFT_URL_RE = /\/editor\/drafts\/(\d+)/;
63
+ const LIVE_SIGNAL_RE = /juejin\.cn\/(post|published)\//;
64
+ const LIVE_URL_ID_RE = /juejin\.cn\/(?:post|published)\/(\d+)/;
65
+ const DRAFT_TOASTS = ['保存成功'];
66
+ const LIVE_TOASTS = ['发布成功'];
67
+
68
+ /**
69
+ * Juejin-owned readiness predicate: the editor is reachable (no login redirect) and the title field
70
+ * is attached. Shared by the publish preflight and the profile-session CLI (R1, R5).
71
+ */
72
+ export const isAuthenticated: IsAuthenticatedPredicate = async (page) =>
73
+ !LOGIN_URL_RE.test(page.url()) && (await trySelectors(page, SELECTORS.title, { visible: false })).found;
74
+
75
+ /** Site-owned config for the shared session check and operator CLI (0183). */
76
+ export const CONFIG: ChannelBrowserConfig = {
77
+ channel: CHANNEL,
78
+ homeUrl: EDITOR_URL,
79
+ isAuthenticated,
80
+ loginTimeoutMs: LOGIN_TIMEOUT_MS,
81
+ };
82
+
83
+ /** Validated live-run inputs: the publish popup cannot confirm without both (R4). */
84
+ interface LiveInputs {
85
+ category: string;
86
+ tags: string[];
87
+ }
88
+
89
+ /**
90
+ * One run shape: a draft run needs nothing beyond the content, a live run carries the validated
91
+ * popup inputs — so "live without a category or tags" is not a representable state.
92
+ */
93
+ type EditorRun =
94
+ | { publishStatus: 'draft'; profileDir: string }
95
+ | { publishStatus: 'public'; live: LiveInputs; profileDir: string };
16
96
 
17
97
  function fail(error: string): Result {
18
98
  return ResultSchema.parse({ ok: false, target: TARGET, error });
19
99
  }
20
100
 
21
- function draftId(content: Content): string {
22
- const title = content.title ?? 'untitled';
23
- return `${CHANNEL}-draft-${title.length}`;
101
+ function errorMessage(error: unknown): string {
102
+ return error instanceof Error ? error.message : String(error);
24
103
  }
25
104
 
26
- function draftResult(content: Content, driver: BrowserDriver, profileDir: string): Result {
27
- return ResultSchema.parse({
28
- ok: true,
29
- target: TARGET,
30
- id: draftId(content),
31
- message: `${TARGET} draft prepared (unpublished)`,
32
- metadata: { driver, publishStatus: 'draft', profileDir },
33
- });
105
+ /** Session failures whose one-liner must name the recovery command (R5). */
106
+ function needsLogin(error: unknown): boolean {
107
+ return error instanceof LoginTimeoutError || error instanceof BrowserProfileMissingError;
108
+ }
109
+
110
+ /**
111
+ * `确定并发布` needs a category and at least one tag, so a live run names the missing field instead
112
+ * of opening a browser it cannot finish in. Returns the validated inputs, or the failure message.
113
+ */
114
+ function liveInputs(content: Content): LiveInputs | string {
115
+ const category = content.metadata?.category;
116
+ if (typeof category !== 'string' || category.trim().length === 0) {
117
+ return 'live publish requires content.metadata.category (a Juejin category name)';
118
+ }
119
+ const raw = content.metadata?.tags;
120
+ const tags = Array.isArray(raw)
121
+ ? raw.filter((tag): tag is string => typeof tag === 'string' && tag.trim().length > 0)
122
+ : [];
123
+ // An absent, empty or partly unusable tag list is named as the missing field rather than
124
+ // silently trimmed down to the tags that happened to parse.
125
+ if (!Array.isArray(raw) || tags.length === 0 || tags.length !== raw.length) {
126
+ return 'live publish requires content.metadata.tags (at least one non-empty tag)';
127
+ }
128
+ return { category: category.trim(), tags };
129
+ }
130
+
131
+ /**
132
+ * Text of the first visible toast among `texts`, or null. `:text(…)` matches on a substring, so
133
+ * the element's own text is returned rather than the candidate literal.
134
+ */
135
+ /**
136
+ * The draft/live outcome, read only from the page's notification containers (residual 0196): an
137
+ * unscoped `:text(...)` match also hits the article body this flow filled a few steps earlier, so
138
+ * a body containing `保存成功` reported a draft that was never saved.
139
+ */
140
+ async function readToast(page: Page, texts: readonly string[]): Promise<string | null> {
141
+ return readScopedSignal(page, texts);
34
142
  }
35
143
 
36
144
  export class JuejinBrowserTransport implements PublishTransport {
37
145
  public readonly kind = 'browser';
38
146
  public readonly driver: BrowserDriver;
39
- private readonly loginTimeoutMs: number;
40
- private readonly sessionReady: boolean;
147
+ private readonly launcher?: BrowserLauncher;
148
+ private readonly stepTimeoutMs: number;
41
149
 
42
- constructor(driver: BrowserDriver = 'agent', opts?: { loginTimeoutMs?: number; sessionReady?: boolean }) {
150
+ constructor(driver: BrowserDriver = 'script', deps: { launcher?: BrowserLauncher; stepTimeoutMs?: number } = {}) {
43
151
  this.driver = driver;
44
- this.loginTimeoutMs = opts?.loginTimeoutMs ?? 120_000;
45
- this.sessionReady = opts?.sessionReady ?? false;
152
+ this.launcher = deps.launcher;
153
+ this.stepTimeoutMs = deps.stepTimeoutMs ?? STEP_TIMEOUT_MS;
46
154
  }
47
155
 
48
156
  public async publish(payload: PublishTransportPayload): Promise<Result> {
49
- const override =
50
- typeof payload.options?.profileDir === 'string'
51
- ? payload.options.profileDir
52
- : (process.env.JUEJIN_PROFILE_DIR ?? process.env.KK_BROWSER_PROFILE_DIR);
53
- const profileDir = resolveBrowserProfileDir(CHANNEL, override);
54
- if (!this.sessionReady) {
55
- return fail(
56
- `login timeout after ${this.loginTimeoutMs}ms: no authenticated ${CHANNEL} session in ${profileDir}`,
157
+ if (this.driver !== 'script') {
158
+ return fail(`unsupported driver '${this.driver}' for ${TARGET}`);
159
+ }
160
+ const requested = payload.content.metadata?.publishStatus;
161
+ if (requested !== undefined && requested !== 'draft' && requested !== 'public') {
162
+ return fail(`unknown publishStatus '${String(requested)}' (expected absent, 'draft' or 'public')`);
163
+ }
164
+ // Live pre-validation, before any browser opens: the popup's two prerequisites (R4).
165
+ let live: LiveInputs | undefined;
166
+ if (requested === 'public') {
167
+ const inputs = liveInputs(payload.content);
168
+ if (typeof inputs === 'string') return fail(inputs);
169
+ live = inputs;
170
+ }
171
+ const override = typeof payload.options?.profileDir === 'string' ? payload.options.profileDir : undefined;
172
+ const profileDir = resolveChannelProfileDir(CHANNEL, override);
173
+
174
+ // ADR-025 preflight: an unauthenticated or absent profile must never produce a headed
175
+ // visit, so the read-only headless check runs first and a refusal stays a fast refusal.
176
+ const session = await checkChannelSession(CONFIG, {
177
+ profileDir,
178
+ launcher: this.launcher,
179
+ headedFallback: false,
180
+ timeoutMs: this.stepTimeoutMs,
181
+ });
182
+ if (session.status !== 'verified') {
183
+ return fail(`${CHANNEL} session preflight refused: ${session.detail} — run: ${LOGIN_INSTRUCTION}`);
184
+ }
185
+
186
+ const run: EditorRun =
187
+ live === undefined ? { publishStatus: 'draft', profileDir } : { publishStatus: 'public', live, profileDir };
188
+ try {
189
+ return await withProfilePage(
190
+ {
191
+ profileDir,
192
+ url: EDITOR_URL,
193
+ isAuthenticated,
194
+ // Headed by default (sites bot-wall headless loads); the headless preflight
195
+ // above is the unattended pass, `options.headless === true` the opt-in.
196
+ headless: payload.options?.headless === true,
197
+ launcher: this.launcher,
198
+ // The session is already preflight-verified, so the post-goto confirmation is
199
+ // a bounded step like every other one, not the 180s interactive login wait.
200
+ timeoutMs: this.stepTimeoutMs,
201
+ },
202
+ async ({ page }) => this.runEditorFlow(page, payload.content, run),
57
203
  );
204
+ } catch (error) {
205
+ const hint = needsLogin(error) ? ` — run: ${LOGIN_INSTRUCTION}` : '';
206
+ return fail(`${errorMessage(error)}${hint}`);
58
207
  }
59
- return draftResult(payload.content, this.driver, profileDir);
60
208
  }
209
+
210
+ /**
211
+ * The site-owned editor flow: title → markdown body → draft signal, then — for a live run only
212
+ * — `发布` → category → tags → `确定并发布` → live signal. Every outcome comes from an
213
+ * `expectOutcome` probe, so a wait or a click without an observed signal is never a success
214
+ * (E91-D6), and a draft run never touches a live control (E91-D5).
215
+ */
216
+ private async runEditorFlow(page: Page, content: Content, run: EditorRun): Promise<Result> {
217
+ const metadata = { driver: this.driver, publishStatus: run.publishStatus, profileDir: run.profileDir };
218
+
219
+ const titleSelector = await resolveSelector(page, SELECTORS.title, {
220
+ timeoutMs: this.stepTimeoutMs,
221
+ label: 'title input',
222
+ });
223
+ await page
224
+ .locator(titleSelector)
225
+ .first()
226
+ .fill(content.title ?? '');
227
+
228
+ const bodySelector = await resolveSelector(page, SELECTORS.body, {
229
+ timeoutMs: this.stepTimeoutMs,
230
+ label: 'body editor',
231
+ });
232
+ // bytemd is CodeMirror over markdown: the body goes in as markdown, never as HTML (E91-D4).
233
+ await fillCodeMirror(page, bodySelector, content.body, { timeoutMs: this.stepTimeoutMs });
234
+
235
+ // Draft evidence (E91-D6): the autosave gets its window, then the manual save key is pressed
236
+ // once when the page showed nothing. Success still needs the observed signal.
237
+ const autosaveMs = Math.min(AUTOSAVE_TIMEOUT_MS, this.stepTimeoutMs);
238
+ if (!(await waitUntil(async () => (await probeDraft(page)) !== null, autosaveMs))) {
239
+ await page.keyboard.press(SAVE_KEY);
240
+ }
241
+ const draft = await expectOutcome(() => probeDraft(page), {
242
+ timeoutMs: this.stepTimeoutMs,
243
+ label: `juejin draft save in '${bodySelector}'`,
244
+ });
245
+ if (!draft.ok) return fail(draft.error);
246
+
247
+ if (run.publishStatus === 'draft') {
248
+ const url = page.url();
249
+ const id = idFromUrl(url);
250
+ return ResultSchema.parse({
251
+ ok: true,
252
+ target: TARGET,
253
+ ...(id === undefined ? {} : { id }),
254
+ url,
255
+ message: `${TARGET} draft saved (unpublished) in profile ${run.profileDir}`,
256
+ metadata,
257
+ });
258
+ }
259
+
260
+ const publishSelector = await resolveSelector(page, SELECTORS.publish, {
261
+ timeoutMs: this.stepTimeoutMs,
262
+ label: 'publish control',
263
+ });
264
+ await page.locator(publishSelector).first().click();
265
+ await fillPublishPopup(page, run.live, this.stepTimeoutMs);
266
+
267
+ const confirmSelector = await resolveSelector(page, SELECTORS.confirm, {
268
+ timeoutMs: this.stepTimeoutMs,
269
+ label: 'confirm control',
270
+ });
271
+ await page.locator(confirmSelector).first().click();
272
+ const live = await expectOutcome(() => probeLive(page), {
273
+ timeoutMs: this.stepTimeoutMs,
274
+ label: `juejin publish '${confirmSelector}'`,
275
+ });
276
+ if (!live.ok) return fail(live.error);
277
+
278
+ const url = page.url();
279
+ const id = idFromUrl(url);
280
+ return ResultSchema.parse({
281
+ ok: true,
282
+ target: TARGET,
283
+ ...(id === undefined ? {} : { id }),
284
+ url,
285
+ message: `${TARGET} published live in profile ${run.profileDir}`,
286
+ metadata,
287
+ });
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Live popup (E91-D5): the category item whose text is exactly `metadata.category`, then every tag
293
+ * typed into the tag input with `Enter`. Runs only on the `public` path — `发布` opens this popup.
294
+ */
295
+ async function fillPublishPopup(page: Page, live: LiveInputs, timeoutMs: number): Promise<void> {
296
+ await resolveSelector(page, [SELECTORS.category], { timeoutMs, label: 'category list' });
297
+ const items = page.locator(SELECTORS.category);
298
+ const total = await items.count();
299
+ let picked = false;
300
+ for (let index = 0; index < total; index += 1) {
301
+ const item = items.nth(index);
302
+ if (((await item.textContent()) ?? '').trim() === live.category) {
303
+ await item.click();
304
+ picked = true;
305
+ break;
306
+ }
307
+ }
308
+ if (!picked) {
309
+ throw new Error(`category '${live.category}': no '${SELECTORS.category}' item matched its exact text`);
310
+ }
311
+
312
+ const tagSelector = await resolveSelector(page, [SELECTORS.tagInput], { timeoutMs, label: 'tag input' });
313
+ for (const tag of live.tags) {
314
+ const input = page.locator(tagSelector).first();
315
+ await input.fill(tag);
316
+ await input.press('Enter');
317
+ }
318
+ }
319
+
320
+ /** The article id the observed URL carries: `/editor/drafts/<id>` (draft) or `/post/<id>` (live). */
321
+ function idFromUrl(url: string): string | undefined {
322
+ return DRAFT_URL_RE.exec(url)?.[1] ?? LIVE_URL_ID_RE.exec(url)?.[1];
323
+ }
324
+
325
+ /** Draft evidence (E91-D6): the editor URL moved onto a draft id, or a visible `保存成功` toast. */
326
+ async function probeDraft(page: Page): Promise<string | null> {
327
+ const url = page.url();
328
+ if (DRAFT_URL_RE.test(url)) return url;
329
+ return readToast(page, DRAFT_TOASTS);
330
+ }
331
+
332
+ /** Live evidence (E91-D6): a `/post/`- or `/published/`-shaped URL, or a visible `发布成功` toast. */
333
+ async function probeLive(page: Page): Promise<string | null> {
334
+ const url = page.url();
335
+ if (LIVE_SIGNAL_RE.test(url)) return url;
336
+ return readToast(page, LIVE_TOASTS);
61
337
  }
62
338
 
63
- export function getPublishTransport(driver: BrowserDriver = 'agent'): PublishTransport {
339
+ export function getPublishTransport(driver: BrowserDriver = 'script'): PublishTransport {
64
340
  if (process.env.KNOWLEDGE_KIT_PUBLISH_TRANSPORT === 'fake') {
65
- return new FakeBrowserTransport(driver);
341
+ return new FakeBrowserTransport('script');
66
342
  }
67
343
  return new JuejinBrowserTransport(driver);
68
344
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Juejin profile-session capability (R5, AC3).
3
+ *
4
+ * `login` opens the editor in a headed browser and resolves only once Juejin's readiness predicate
5
+ * passes; `check` reopens the profile headlessly and reports `verified` or `refused`. Arg parsing,
6
+ * profile precedence (`--profile` > `JUEJIN_PROFILE_DIR` > `KK_BROWSER_PROFILE_DIR` >
7
+ * `~/.config/kk/browser-profiles/juejin`) and the exit codes (0 verified, 1 refused, 2 usage error)
8
+ * all live in the shared `runProfileCli` (0183), so the publish preflight and this CLI apply the
9
+ * same gate.
10
+ *
11
+ * Operator dogfood (plan step 6, handed to 0192): run `profile-check.ts login`, then `check`, on
12
+ * the same profile, and re-measure the editor URL, the selectors and the draft signal.
13
+ */
14
+ import { runProfileCli } from '@gobing-ai/publish-harness/browser';
15
+ import { CONFIG } from './index';
16
+
17
+ if (import.meta.main) {
18
+ process.exit(await runProfileCli(CONFIG, Bun.argv.slice(2)));
19
+ }
@@ -14642,7 +14642,7 @@ var init_zod = __esm(() => {
14642
14642
  init_external();
14643
14643
  });
14644
14644
 
14645
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/errors.js
14645
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/errors.js
14646
14646
  var ErrorCode;
14647
14647
  var init_errors3 = __esm(() => {
14648
14648
  ErrorCode = {
@@ -14653,7 +14653,7 @@ var init_errors3 = __esm(() => {
14653
14653
  };
14654
14654
  });
14655
14655
 
14656
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/api-response.js
14656
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/api-response.js
14657
14657
  var API_ERROR_CODES, ERROR_CODE_TO_HTTP, CLIENT_SAFE_CODES;
14658
14658
  var init_api_response = __esm(() => {
14659
14659
  init_errors3();
@@ -14676,13 +14676,13 @@ var init_api_response = __esm(() => {
14676
14676
  CLIENT_SAFE_CODES = new Set([ErrorCode.NotFound, ErrorCode.Validation, ErrorCode.Conflict]);
14677
14677
  });
14678
14678
 
14679
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/cursor.js
14679
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/cursor.js
14680
14680
  var init_cursor = () => {};
14681
14681
 
14682
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/event-severity.js
14682
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/event-severity.js
14683
14683
  var init_event_severity = () => {};
14684
14684
 
14685
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/output.js
14685
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/output.js
14686
14686
  function processStream(name) {
14687
14687
  const proc = globalThis.process;
14688
14688
  const stream = proc?.[name];
@@ -14700,7 +14700,7 @@ function echoError(message, target = defaultStderrTarget ?? processStream("stder
14700
14700
  }
14701
14701
  var defaultStderrTarget;
14702
14702
 
14703
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/index.js
14703
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/index.js
14704
14704
  var init_dist = __esm(() => {
14705
14705
  init_api_response();
14706
14706
  init_cursor();
@@ -21698,7 +21698,7 @@ var init_dist2 = __esm(() => {
21698
21698
  $visitAsync = visit.visitAsync;
21699
21699
  });
21700
21700
 
21701
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.11/node_modules/@gobing-ai/ts-runtime/dist/config.js
21701
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/config.js
21702
21702
  var configSchema;
21703
21703
  var init_config = __esm(() => {
21704
21704
  init_dist2();
@@ -21722,7 +21722,7 @@ var init_config = __esm(() => {
21722
21722
  });
21723
21723
  });
21724
21724
 
21725
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.11/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21725
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21726
21726
  import { appendFileSync, cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
21727
21727
  import { dirname, resolve as resolvePath } from "path";
21728
21728
  function createNodeFileSystem(root, paths) {
@@ -21900,7 +21900,7 @@ var PluginManifestSchema = exports_external.looseObject({
21900
21900
  description: exports_external.string().optional(),
21901
21901
  operations: exports_external.record(exports_external.string(), PluginOperationSchema).optional()
21902
21902
  });
21903
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.11/node_modules/@gobing-ai/ts-runtime/dist/index.js
21903
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/index.js
21904
21904
  init_file_system_node();
21905
21905
  init_config();
21906
21906
 
@@ -14642,7 +14642,7 @@ var init_zod = __esm(() => {
14642
14642
  init_external();
14643
14643
  });
14644
14644
 
14645
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/errors.js
14645
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/errors.js
14646
14646
  var ErrorCode;
14647
14647
  var init_errors3 = __esm(() => {
14648
14648
  ErrorCode = {
@@ -14653,7 +14653,7 @@ var init_errors3 = __esm(() => {
14653
14653
  };
14654
14654
  });
14655
14655
 
14656
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/api-response.js
14656
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/api-response.js
14657
14657
  var API_ERROR_CODES, ERROR_CODE_TO_HTTP, CLIENT_SAFE_CODES;
14658
14658
  var init_api_response = __esm(() => {
14659
14659
  init_errors3();
@@ -14676,13 +14676,13 @@ var init_api_response = __esm(() => {
14676
14676
  CLIENT_SAFE_CODES = new Set([ErrorCode.NotFound, ErrorCode.Validation, ErrorCode.Conflict]);
14677
14677
  });
14678
14678
 
14679
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/cursor.js
14679
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/cursor.js
14680
14680
  var init_cursor = () => {};
14681
14681
 
14682
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/event-severity.js
14682
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/event-severity.js
14683
14683
  var init_event_severity = () => {};
14684
14684
 
14685
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/output.js
14685
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/output.js
14686
14686
  function processStream(name) {
14687
14687
  const proc = globalThis.process;
14688
14688
  const stream = proc?.[name];
@@ -14700,7 +14700,7 @@ function echoError(message, target = defaultStderrTarget ?? processStream("stder
14700
14700
  }
14701
14701
  var defaultStderrTarget;
14702
14702
 
14703
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/index.js
14703
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/index.js
14704
14704
  var init_dist = __esm(() => {
14705
14705
  init_api_response();
14706
14706
  init_cursor();
@@ -21698,7 +21698,7 @@ var init_dist2 = __esm(() => {
21698
21698
  $visitAsync = visit.visitAsync;
21699
21699
  });
21700
21700
 
21701
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.11/node_modules/@gobing-ai/ts-runtime/dist/config.js
21701
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/config.js
21702
21702
  var configSchema;
21703
21703
  var init_config = __esm(() => {
21704
21704
  init_dist2();
@@ -21722,7 +21722,7 @@ var init_config = __esm(() => {
21722
21722
  });
21723
21723
  });
21724
21724
 
21725
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.11/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21725
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21726
21726
  import { appendFileSync, cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
21727
21727
  import { dirname, resolve as resolvePath } from "path";
21728
21728
  function createNodeFileSystem(root, paths) {
@@ -21902,7 +21902,7 @@ var PluginManifestSchema = exports_external.looseObject({
21902
21902
  description: exports_external.string().optional(),
21903
21903
  operations: exports_external.record(exports_external.string(), PluginOperationSchema).optional()
21904
21904
  });
21905
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.11/node_modules/@gobing-ai/ts-runtime/dist/index.js
21905
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/index.js
21906
21906
  init_file_system_node();
21907
21907
  init_config();
21908
21908
 
@@ -14642,7 +14642,7 @@ var init_zod = __esm(() => {
14642
14642
  init_external();
14643
14643
  });
14644
14644
 
14645
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/errors.js
14645
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/errors.js
14646
14646
  var ErrorCode;
14647
14647
  var init_errors3 = __esm(() => {
14648
14648
  ErrorCode = {
@@ -14653,7 +14653,7 @@ var init_errors3 = __esm(() => {
14653
14653
  };
14654
14654
  });
14655
14655
 
14656
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/api-response.js
14656
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/api-response.js
14657
14657
  var API_ERROR_CODES, ERROR_CODE_TO_HTTP, CLIENT_SAFE_CODES;
14658
14658
  var init_api_response = __esm(() => {
14659
14659
  init_errors3();
@@ -14676,13 +14676,13 @@ var init_api_response = __esm(() => {
14676
14676
  CLIENT_SAFE_CODES = new Set([ErrorCode.NotFound, ErrorCode.Validation, ErrorCode.Conflict]);
14677
14677
  });
14678
14678
 
14679
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/cursor.js
14679
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/cursor.js
14680
14680
  var init_cursor = () => {};
14681
14681
 
14682
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/event-severity.js
14682
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/event-severity.js
14683
14683
  var init_event_severity = () => {};
14684
14684
 
14685
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/output.js
14685
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/output.js
14686
14686
  function processStream(name) {
14687
14687
  const proc = globalThis.process;
14688
14688
  const stream = proc?.[name];
@@ -14700,7 +14700,7 @@ function echoError(message, target = defaultStderrTarget ?? processStream("stder
14700
14700
  }
14701
14701
  var defaultStderrTarget;
14702
14702
 
14703
- // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.11/node_modules/@gobing-ai/ts-utils/dist/index.js
14703
+ // ../../node_modules/.bun/@gobing-ai+ts-utils@0.5.15/node_modules/@gobing-ai/ts-utils/dist/index.js
14704
14704
  var init_dist = __esm(() => {
14705
14705
  init_api_response();
14706
14706
  init_cursor();
@@ -21698,7 +21698,7 @@ var init_dist2 = __esm(() => {
21698
21698
  $visitAsync = visit.visitAsync;
21699
21699
  });
21700
21700
 
21701
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.11/node_modules/@gobing-ai/ts-runtime/dist/config.js
21701
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/config.js
21702
21702
  var configSchema;
21703
21703
  var init_config = __esm(() => {
21704
21704
  init_dist2();
@@ -21722,7 +21722,7 @@ var init_config = __esm(() => {
21722
21722
  });
21723
21723
  });
21724
21724
 
21725
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.11/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21725
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/file-system-node.js
21726
21726
  import { appendFileSync, cpSync, createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
21727
21727
  import { dirname, resolve as resolvePath } from "path";
21728
21728
  function createNodeFileSystem(root, paths) {
@@ -21900,7 +21900,7 @@ var PluginManifestSchema = exports_external.looseObject({
21900
21900
  description: exports_external.string().optional(),
21901
21901
  operations: exports_external.record(exports_external.string(), PluginOperationSchema).optional()
21902
21902
  });
21903
- // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.11/node_modules/@gobing-ai/ts-runtime/dist/index.js
21903
+ // ../../node_modules/.bun/@gobing-ai+ts-runtime@0.5.15/node_modules/@gobing-ai/ts-runtime/dist/index.js
21904
21904
  init_file_system_node();
21905
21905
  init_config();
21906
21906