@lightcone-ai/daemon 0.9.71 → 0.9.72

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.
@@ -2,6 +2,7 @@
2
2
  * Douyin (抖音) publisher adapter.
3
3
  * Uses 抖音创作服务平台: https://creator.douyin.com
4
4
  */
5
+ import { formatTextWithTags } from '../text.js';
5
6
 
6
7
  function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
7
8
 
@@ -51,7 +52,7 @@ export class DouyinAdapter {
51
52
  await sleep(3000);
52
53
  }
53
54
 
54
- const fullText = `${text}${tags.length > 0 ? '\n' + tags.map(t => `#${t}`).join(' ') : ''}`;
55
+ const fullText = formatTextWithTags(text, tags);
55
56
  await this._fillField('[class*="content"] [contenteditable], [placeholder*="添加作品描述"]', fullText);
56
57
 
57
58
  if (title) {
@@ -78,7 +79,7 @@ export class DouyinAdapter {
78
79
  await this._uploadFiles([video], 'video');
79
80
  await this._waitForText('上传成功', 120000);
80
81
 
81
- const fullText = `${text}${tags.length > 0 ? '\n' + tags.map(t => `#${t}`).join(' ') : ''}`;
82
+ const fullText = formatTextWithTags(text, tags);
82
83
  await this._fillField('[placeholder*="添加作品描述"], [class*="content"] [contenteditable]', fullText);
83
84
 
84
85
  if (title) {
@@ -2,6 +2,7 @@
2
2
  * Kuaishou (快手) publisher adapter.
3
3
  * Uses 快手创作者平台: https://cp.kuaishou.com
4
4
  */
5
+ import { formatTextWithTags } from '../text.js';
5
6
 
6
7
  function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
7
8
 
@@ -55,7 +56,7 @@ export class KuaishouAdapter {
55
56
  await this._fillField('[placeholder*="标题"]', title);
56
57
  }
57
58
 
58
- const fullText = `${text}${tags.length > 0 ? '\n' + tags.map(t => `#${t}`).join(' ') : ''}`;
59
+ const fullText = formatTextWithTags(text, tags);
59
60
  await this._fillField('[placeholder*="描述"], [contenteditable]', fullText);
60
61
 
61
62
  await sleep(1000);
@@ -82,7 +83,7 @@ export class KuaishouAdapter {
82
83
  await this._fillField('[placeholder*="标题"]', title);
83
84
  }
84
85
 
85
- const fullText = `${text}${tags.length > 0 ? '\n' + tags.map(t => `#${t}`).join(' ') : ''}`;
86
+ const fullText = formatTextWithTags(text, tags);
86
87
  await this._fillField('[placeholder*="描述"], [contenteditable]', fullText);
87
88
 
88
89
  await sleep(1000);
@@ -2,6 +2,7 @@
2
2
  * XHS (小红书) publisher adapter.
3
3
  * Uses deterministic CDP operations — no AI vision required.
4
4
  */
5
+ import { formatTextWithTags } from '../text.js';
5
6
 
6
7
  function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
7
8
  function randomInt(min, max) { return Math.floor(min + Math.random() * (max - min + 1)); }
@@ -97,8 +98,8 @@ export class XhsAdapter {
97
98
  await humanPause(1200, 2800, 'after-title');
98
99
  }
99
100
 
100
- // Fill content text + tags
101
- const fullText = tags.length > 0 ? `${text}\n${tags.map(t => `#${t}`).join(' ')}` : text;
101
+ // Fill content text and only append tags that are not already present.
102
+ const fullText = formatTextWithTags(text, tags);
102
103
  await this._fillField(CONTENT_SELECTOR, fullText);
103
104
 
104
105
  await humanPause(4500, 9000, 'before-publish-check');
@@ -142,7 +143,7 @@ export class XhsAdapter {
142
143
  await humanPause(1200, 2800, 'after-title');
143
144
  }
144
145
 
145
- const fullText = tags.length > 0 ? `${text}\n${tags.map(t => `#${t}`).join(' ')}` : text;
146
+ const fullText = formatTextWithTags(text, tags);
146
147
  await this._fillField(CONTENT_SELECTOR, fullText);
147
148
 
148
149
  await humanPause(4500, 9000, 'before-publish-check');
@@ -0,0 +1,27 @@
1
+ function escapeRegExp(value) {
2
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
3
+ }
4
+
5
+ export function normalizeTags(tags = []) {
6
+ const seen = new Set();
7
+ const normalized = [];
8
+ for (const raw of tags) {
9
+ const tag = String(raw ?? '').trim().replace(/^[##]+/, '').trim();
10
+ if (!tag || seen.has(tag)) continue;
11
+ seen.add(tag);
12
+ normalized.push(tag);
13
+ }
14
+ return normalized;
15
+ }
16
+
17
+ export function formatTextWithTags(text, tags = []) {
18
+ const base = String(text ?? '').trimEnd();
19
+ const missingTags = normalizeTags(tags).filter(tag => {
20
+ const pattern = new RegExp(`(^|[\\s,,。!?!?::;;、])(?:#|#)${escapeRegExp(tag)}(?=$|[\\s,,。!?!?::;;、])`);
21
+ return !pattern.test(base);
22
+ });
23
+
24
+ if (missingTags.length === 0) return base;
25
+ const suffix = missingTags.map(tag => `#${tag}`).join(' ');
26
+ return base ? `${base}\n${suffix}` : suffix;
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.9.71",
3
+ "version": "0.9.72",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {