@lightcone-ai/daemon 0.15.37 → 0.15.39

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.
@@ -123,6 +123,9 @@ export class BilibiliAdapter {
123
123
  if (!loggedIn) throw new Error('LOGIN_EXPIRED: B站登录已过期,请重新扫码连接');
124
124
 
125
125
  if (images.length > 0) {
126
+ // The file input only appears after clicking the image button in the editor toolbar
127
+ await this._clickImageToolbarButton();
128
+ await sleep(1_000);
126
129
  await this._waitForSelector(ARTICLE_IMAGE_FILE_SELECTOR, 15_000);
127
130
  await this._uploadFiles(images, 'image');
128
131
  await this._waitForUploadSettled(180_000);
@@ -326,6 +329,45 @@ export class BilibiliAdapter {
326
329
  await sleep(400);
327
330
  }
328
331
 
332
+ async _clickImageToolbarButton() {
333
+ const result = await this._cdp.send('Runtime.evaluate', {
334
+ expression: `
335
+ (function() {
336
+ const selectors = [
337
+ '.ql-image',
338
+ '[class*="toolbar-image"]',
339
+ '[class*="toolbar"] [class*="image"]',
340
+ '[class*="toolbar"] [class*="pic"]',
341
+ '[aria-label="图片"]',
342
+ '[aria-label*="插入图片"]',
343
+ '[title="图片"]',
344
+ '[title*="插入图片"]',
345
+ '[class*="bf-icon-image"]',
346
+ '[data-action="uploadImage"]',
347
+ '[data-type="image"]',
348
+ ];
349
+ for (const sel of selectors) {
350
+ const el = document.querySelector(sel);
351
+ if (el) { el.click(); return sel; }
352
+ }
353
+ // Heuristic: any toolbar button whose title/aria-label mentions image/图片
354
+ for (const el of document.querySelectorAll('[class*="toolbar"] button, [class*="toolbar"] [role="button"], [class*="toolbar"] span, [class*="editor-toolbar"] *')) {
355
+ const hint = [el.getAttribute('title'), el.getAttribute('aria-label'), el.className?.toString?.()].filter(Boolean).join(' ').toLowerCase();
356
+ if (hint.includes('image') || hint.includes('图片') || hint.includes('pic')) {
357
+ el.click();
358
+ return 'toolbar-heuristic:' + hint.slice(0, 60);
359
+ }
360
+ }
361
+ return null;
362
+ })()
363
+ `,
364
+ returnByValue: true,
365
+ });
366
+ const clicked = result.result?.value;
367
+ console.error(`[BilibiliAdapter] _clickImageToolbarButton: ${clicked ?? 'not found'}`);
368
+ return !!clicked;
369
+ }
370
+
329
371
  async _clickByTextCandidates(candidates = []) {
330
372
  for (const text of candidates) {
331
373
  const clicked = await this._clickByText(text);
@@ -128,7 +128,19 @@ export class KuaishouAdapter {
128
128
  const deadline = Date.now() + timeoutMs;
129
129
  while (Date.now() < deadline) {
130
130
  const result = await this._cdp.send('Runtime.evaluate', {
131
- expression: `!!document.querySelector(${JSON.stringify(selector)})`,
131
+ expression: `
132
+ (function() {
133
+ const sel = ${JSON.stringify(selector)};
134
+ if (document.querySelector(sel)) return true;
135
+ for (const f of document.querySelectorAll('iframe')) {
136
+ try {
137
+ const d = f.contentDocument || f.contentWindow?.document;
138
+ if (d && d.querySelector(sel)) return true;
139
+ } catch(e) {}
140
+ }
141
+ return false;
142
+ })()
143
+ `,
132
144
  returnByValue: true,
133
145
  });
134
146
  if (result.result?.value) return;
@@ -136,12 +148,16 @@ export class KuaishouAdapter {
136
148
  }
137
149
  // Dump diagnostic info to help debug page state on timeout
138
150
  try {
139
- const [urlR, titleR, bodyR, inputR, uploadR] = await Promise.all([
151
+ const [urlR, titleR, bodyR, inputR, uploadR, iframeR] = await Promise.all([
140
152
  this._cdp.send('Runtime.evaluate', { expression: 'location.href', returnByValue: true }),
141
153
  this._cdp.send('Runtime.evaluate', { expression: 'document.title', returnByValue: true }),
142
154
  this._cdp.send('Runtime.evaluate', { expression: 'document.body?.innerText?.slice(0,400)', returnByValue: true }),
143
155
  this._cdp.send('Runtime.evaluate', { expression: `!!document.querySelector('input[type="file"]')`, returnByValue: true }),
144
156
  this._cdp.send('Runtime.evaluate', { expression: `document.querySelectorAll('[class*="upload"],[class*="Upload"]').length`, returnByValue: true }),
157
+ this._cdp.send('Runtime.evaluate', {
158
+ expression: `(function(){const frames=document.querySelectorAll('iframe');let found=false;for(const f of frames){try{const d=f.contentDocument||f.contentWindow?.document;if(d&&d.querySelector('input[type="file"]')){found=true;break;}}catch(e){}}return JSON.stringify({iframes:frames.length,fileInIframe:found})})()`,
159
+ returnByValue: true,
160
+ }),
145
161
  ]);
146
162
  console.error(`[KuaishouAdapter] selector timeout diagnostics:`);
147
163
  console.error(` url=${urlR.result?.value}`);
@@ -149,6 +165,7 @@ export class KuaishouAdapter {
149
165
  console.error(` input[type=file] present=${inputR.result?.value}`);
150
166
  console.error(` upload-class elements=${uploadR.result?.value}`);
151
167
  console.error(` body text: ${bodyR.result?.value}`);
168
+ console.error(` iframe info: ${iframeR.result?.value}`);
152
169
  } catch (diagErr) {
153
170
  console.error(`[KuaishouAdapter] diagnostic failed: ${diagErr.message}`);
154
171
  }
@@ -196,12 +213,27 @@ export class KuaishouAdapter {
196
213
  }
197
214
 
198
215
  async _uploadFiles(filePaths) {
199
- // Wait up to 10s for the file input to appear (may be hidden/dynamically rendered)
216
+ // Wait up to 10s for the file input; check main document and same-origin iframes
200
217
  const deadline = Date.now() + 10000;
201
218
  let objectId = null;
202
219
  while (Date.now() < deadline) {
203
220
  const result = await this._cdp.send('Runtime.evaluate', {
204
- expression: `document.querySelector('input[type="file"]')`,
221
+ expression: `
222
+ (function() {
223
+ const el = document.querySelector('input[type="file"]');
224
+ if (el) return el;
225
+ for (const f of document.querySelectorAll('iframe')) {
226
+ try {
227
+ const d = f.contentDocument || f.contentWindow?.document;
228
+ if (d) {
229
+ const found = d.querySelector('input[type="file"]');
230
+ if (found) return found;
231
+ }
232
+ } catch(e) {}
233
+ }
234
+ return null;
235
+ })()
236
+ `,
205
237
  returnByValue: false,
206
238
  });
207
239
  if (result.result?.objectId) { objectId = result.result.objectId; break; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.15.37",
3
+ "version": "0.15.39",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -426,10 +426,22 @@ function cacheKeyFor(method, apiPath, body, {
426
426
  return createHash('sha256').update(payload, 'utf8').digest('hex');
427
427
  }
428
428
 
429
+ const DATA_URI_PREFIX = 'data:';
430
+ const GOVERNANCE_BINARY_PLACEHOLDER = '[binary content stripped]';
431
+
432
+ function stripBinaryContent(body) {
433
+ if (!body || typeof body !== 'object' || Array.isArray(body)) return body;
434
+ const stripped = { ...body };
435
+ if (typeof stripped.content === 'string' && stripped.content.startsWith(DATA_URI_PREFIX)) {
436
+ stripped.content = GOVERNANCE_BINARY_PLACEHOLDER;
437
+ }
438
+ return stripped;
439
+ }
440
+
429
441
  function toolInputFor(apiPath, body) {
430
442
  return {
431
443
  ...queryParamsFromPath(apiPath),
432
- ...(body && typeof body === 'object' ? body : {}),
444
+ ...stripBinaryContent(body && typeof body === 'object' ? body : {}),
433
445
  };
434
446
  }
435
447