@lightcone-ai/daemon 0.9.65 → 0.9.67
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.
|
@@ -10,8 +10,8 @@ const REQUIREMENTS = {
|
|
|
10
10
|
max_text_length: 1000,
|
|
11
11
|
max_images: 9,
|
|
12
12
|
image_formats: ['jpg', 'jpeg', 'png', 'webp'],
|
|
13
|
-
required_fields: ['text'],
|
|
14
|
-
notes: '
|
|
13
|
+
required_fields: ['text', 'images'],
|
|
14
|
+
notes: '当前自动化支持上传图文,必须提供至少 1 张本地图片;标题建议 2-20 字;图片建议 3:4 竖版;话题标签写在正文末尾如 #话题名',
|
|
15
15
|
},
|
|
16
16
|
short_video: {
|
|
17
17
|
max_text_length: 1000,
|
|
@@ -24,6 +24,7 @@ const REQUIREMENTS = {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
const PUBLISH_URL = 'https://creator.xiaohongshu.com/publish/publish';
|
|
27
|
+
const IMAGE_PUBLISH_URL = 'https://creator.xiaohongshu.com/publish/publish?target=image';
|
|
27
28
|
const VIDEO_PUBLISH_URL = 'https://creator.xiaohongshu.com/publish/publish?source=video';
|
|
28
29
|
|
|
29
30
|
const CREATOR_HOME_URL = 'https://creator.xiaohongshu.com/new/home';
|
|
@@ -135,13 +136,21 @@ export class XhsAdapter {
|
|
|
135
136
|
async _openPublishComposer(kind) {
|
|
136
137
|
const targetTabText = kind === 'image' ? '上传图文' : '上传视频';
|
|
137
138
|
const inputSelector = kind === 'image' ? IMAGE_FILE_INPUT_SELECTOR : VIDEO_FILE_INPUT_SELECTOR;
|
|
138
|
-
const url = kind === 'video' ? VIDEO_PUBLISH_URL :
|
|
139
|
+
const url = kind === 'video' ? VIDEO_PUBLISH_URL : IMAGE_PUBLISH_URL;
|
|
139
140
|
|
|
140
141
|
await this._cdp.send('Page.navigate', { url });
|
|
141
142
|
await this._waitForCreatorShell(20_000);
|
|
142
143
|
await this._assertReadyForPublish();
|
|
143
144
|
|
|
144
|
-
await this.
|
|
145
|
+
if (await this._hasSelector(inputSelector)) return;
|
|
146
|
+
|
|
147
|
+
const clicked = await this._clickVisibleByText(targetTabText, '.creator-tab, button, [role="button"], span, div', { throwOnMissing: false });
|
|
148
|
+
if (clicked) {
|
|
149
|
+
await this._waitForAny([inputSelector], 15_000);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
await this._cdp.send('Page.navigate', { url: kind === 'image' ? `${PUBLISH_URL}?from=tab_switch&target=image` : VIDEO_PUBLISH_URL });
|
|
145
154
|
await this._waitForAny([inputSelector], 15_000);
|
|
146
155
|
}
|
|
147
156
|
|
|
@@ -186,6 +195,14 @@ export class XhsAdapter {
|
|
|
186
195
|
throw new Error(`Timeout waiting for any of: ${combined}`);
|
|
187
196
|
}
|
|
188
197
|
|
|
198
|
+
async _hasSelector(selector) {
|
|
199
|
+
const result = await this._cdp.send('Runtime.evaluate', {
|
|
200
|
+
expression: `!!document.querySelector(${JSON.stringify(selector)})`,
|
|
201
|
+
returnByValue: true,
|
|
202
|
+
});
|
|
203
|
+
return result.result?.value === true;
|
|
204
|
+
}
|
|
205
|
+
|
|
189
206
|
async _waitForText(text, timeoutMs = 8000) {
|
|
190
207
|
const deadline = Date.now() + timeoutMs;
|
|
191
208
|
while (Date.now() < deadline) {
|
|
@@ -364,7 +381,7 @@ export class XhsAdapter {
|
|
|
364
381
|
await sleep(500);
|
|
365
382
|
}
|
|
366
383
|
|
|
367
|
-
async _clickVisibleByText(text, selector) {
|
|
384
|
+
async _clickVisibleByText(text, selector, { throwOnMissing = true } = {}) {
|
|
368
385
|
const result = await this._cdp.send('Runtime.evaluate', {
|
|
369
386
|
expression: `
|
|
370
387
|
(function() {
|
|
@@ -374,7 +391,11 @@ export class XhsAdapter {
|
|
|
374
391
|
return r.width > 0 && r.height > 0 && r.left > -100 && s.display !== 'none' && s.visibility !== 'hidden';
|
|
375
392
|
};
|
|
376
393
|
const els = [...document.querySelectorAll(${JSON.stringify(selector)})].filter(visible);
|
|
377
|
-
const
|
|
394
|
+
const targetText = ${JSON.stringify(text)};
|
|
395
|
+
const el = els.find(e => {
|
|
396
|
+
const t = (e.innerText || e.textContent || '').trim();
|
|
397
|
+
return t === targetText || t.split(/\\s+/).includes(targetText) || t.includes(targetText);
|
|
398
|
+
});
|
|
378
399
|
const target = el?.closest('.creator-tab, button, [role="button"]') || el;
|
|
379
400
|
if (target) { target.click(); return true; }
|
|
380
401
|
return false;
|
|
@@ -382,8 +403,10 @@ export class XhsAdapter {
|
|
|
382
403
|
`,
|
|
383
404
|
returnByValue: true,
|
|
384
405
|
});
|
|
385
|
-
|
|
386
|
-
|
|
406
|
+
const clicked = result.result?.value === true;
|
|
407
|
+
if (!clicked && throwOnMissing) throw new Error(`PUBLISH_FAILED: 找不到小红书发布入口:${text}`);
|
|
408
|
+
if (clicked) await sleep(1000);
|
|
409
|
+
return clicked;
|
|
387
410
|
}
|
|
388
411
|
|
|
389
412
|
async _clickByText(text) {
|
|
@@ -146,6 +146,9 @@ function validateLocalFile(filePath, { kind, required = false, allowedExts = []
|
|
|
146
146
|
function validateMedia({ platform, contentType, images = [], video, cover }) {
|
|
147
147
|
const limits = MEDIA_LIMITS[platform] ?? MEDIA_LIMITS.xhs;
|
|
148
148
|
if (contentType === 'image_text') {
|
|
149
|
+
if (images.length === 0) {
|
|
150
|
+
throw new Error(`image_text requires at least 1 image on ${platform}. Provide absolute image paths inside the agent workspace artifacts directory.`);
|
|
151
|
+
}
|
|
149
152
|
if (images.length > limits.maxImages) throw new Error(`image_text supports at most ${limits.maxImages} images on ${platform}`);
|
|
150
153
|
return {
|
|
151
154
|
images: images.map(filePath => validateLocalFile(filePath, { kind: 'image', required: true, allowedExts: limits.imageExts })),
|