@lightcone-ai/daemon 0.15.9 → 0.15.11
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.
|
@@ -34,13 +34,12 @@ export class KuaishouAdapter {
|
|
|
34
34
|
|
|
35
35
|
async checkLoginStatus() {
|
|
36
36
|
const profileDir = process.env.KUAISHOU_PROFILE_DIR ?? '(not set)';
|
|
37
|
-
|
|
38
|
-
await sleep(4000);
|
|
39
|
-
const urlResult = await this._cdp.send('Runtime.evaluate', { expression: 'window.location.href', returnByValue: true });
|
|
40
|
-
const url = urlResult.result?.value ?? '';
|
|
37
|
+
// Check cookies directly without navigating — avoids leaving the current page
|
|
41
38
|
const result = await this._cdp.send('Network.getAllCookies', {});
|
|
42
39
|
const cookies = result.cookies ?? [];
|
|
43
40
|
const loggedIn = cookies.some(c => (c.name === 'userId' || c.name === 'passToken') && c.value?.length > 0);
|
|
41
|
+
const urlResult = await this._cdp.send('Runtime.evaluate', { expression: 'window.location.href', returnByValue: true });
|
|
42
|
+
const url = urlResult.result?.value ?? '';
|
|
44
43
|
console.error(`[KuaishouAdapter] checkLoginStatus: loggedIn=${loggedIn} url=${url}`);
|
|
45
44
|
return { loggedIn, url, profileDir, userId: null, nickname: null };
|
|
46
45
|
}
|
|
@@ -51,7 +50,7 @@ export class KuaishouAdapter {
|
|
|
51
50
|
await cdp.send('Page.navigate', { url: 'https://cp.kuaishou.com/article/publish/image' });
|
|
52
51
|
await this._waitForSelector('input[type="file"], [class*="upload"]', 12000);
|
|
53
52
|
|
|
54
|
-
const loggedIn = await this.checkLoginStatus();
|
|
53
|
+
const { loggedIn } = await this.checkLoginStatus();
|
|
55
54
|
if (!loggedIn) throw new Error('LOGIN_EXPIRED: 快手登录已过期,请重新扫码连接');
|
|
56
55
|
|
|
57
56
|
if (images.length > 0) {
|
|
@@ -80,7 +79,7 @@ export class KuaishouAdapter {
|
|
|
80
79
|
await cdp.send('Page.navigate', { url: 'https://cp.kuaishou.com/article/publish/video' });
|
|
81
80
|
await this._waitForSelector('input[type="file"], [class*="upload"]', 12000);
|
|
82
81
|
|
|
83
|
-
const loggedIn = await this.checkLoginStatus();
|
|
82
|
+
const { loggedIn } = await this.checkLoginStatus();
|
|
84
83
|
if (!loggedIn) throw new Error('LOGIN_EXPIRED: 快手登录已过期,请重新扫码连接');
|
|
85
84
|
|
|
86
85
|
await this._uploadFiles([video], 'video');
|
|
@@ -155,13 +154,20 @@ export class KuaishouAdapter {
|
|
|
155
154
|
}
|
|
156
155
|
|
|
157
156
|
async _uploadFiles(filePaths) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
// Wait up to 10s for the file input to appear (may be hidden/dynamically rendered)
|
|
158
|
+
const deadline = Date.now() + 10000;
|
|
159
|
+
let objectId = null;
|
|
160
|
+
while (Date.now() < deadline) {
|
|
161
|
+
const result = await this._cdp.send('Runtime.evaluate', {
|
|
162
|
+
expression: `document.querySelector('input[type="file"]')`,
|
|
163
|
+
returnByValue: false,
|
|
164
|
+
});
|
|
165
|
+
if (result.result?.objectId) { objectId = result.result.objectId; break; }
|
|
166
|
+
await sleep(500);
|
|
167
|
+
}
|
|
168
|
+
if (!objectId) throw new Error('No file input found on page after 10s');
|
|
163
169
|
await this._cdp.send('DOM.setFileInputFiles', {
|
|
164
|
-
objectId
|
|
170
|
+
objectId,
|
|
165
171
|
files: filePaths,
|
|
166
172
|
});
|
|
167
173
|
await sleep(500);
|
|
@@ -457,16 +457,19 @@ server.tool(
|
|
|
457
457
|
const adapter = await getAdapter(platform);
|
|
458
458
|
return adapter.checkLoginStatus();
|
|
459
459
|
});
|
|
460
|
+
// Always close session after login check so the Chrome profile is free
|
|
461
|
+
// for the daemon's publish job (which runs in a separate process)
|
|
462
|
+
closeSession(platform);
|
|
460
463
|
if (loggedIn) {
|
|
461
464
|
return { content: [{ type: 'text', text: `✓ ${label} 已登录,可以发布内容。` }] };
|
|
462
465
|
} else {
|
|
463
|
-
closeSession(platform);
|
|
464
466
|
return {
|
|
465
467
|
content: [{ type: 'text', text: `${label} 未登录或登录已过期。\n调试信息: url=${url}\nprofileDir=${profileDir}\n请在「连接外部账号」中重新扫码连接。` }],
|
|
466
468
|
isError: true,
|
|
467
469
|
};
|
|
468
470
|
}
|
|
469
471
|
} catch (err) {
|
|
472
|
+
closeSession(platform);
|
|
470
473
|
return { content: [{ type: 'text', text: `检查失败: ${err.message}` }], isError: true };
|
|
471
474
|
}
|
|
472
475
|
}
|
package/package.json
CHANGED
package/src/chat-bridge.js
CHANGED
|
@@ -22,6 +22,12 @@ import { runRecordUrlNarrationTool } from './record-url-narration-tool.js';
|
|
|
22
22
|
import { runSubmitToLibraryTool } from './submit-to-library-tool.js';
|
|
23
23
|
import { isLeaseInvalidated, clearInvalidatedLease } from './governance-state.js';
|
|
24
24
|
import { classifyLeaseWindow } from './lease-window.js';
|
|
25
|
+
import {
|
|
26
|
+
buildDirectApiHttpError,
|
|
27
|
+
buildDirectApiTransportError,
|
|
28
|
+
shouldEmitToolCallFailed,
|
|
29
|
+
toolCallFailedReason,
|
|
30
|
+
} from './tool-call-failure.js';
|
|
25
31
|
|
|
26
32
|
const cliArgs = process.argv.slice(2);
|
|
27
33
|
function getArg(name) {
|
|
@@ -528,17 +534,22 @@ function enqueueBundleEvent(eventType, payload = {}) {
|
|
|
528
534
|
|
|
529
535
|
async function directApi(method, apiPath, body) {
|
|
530
536
|
const url = `${SERVER_URL}/internal/agent/${AGENT_ID}${apiPath}`;
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
537
|
+
let res;
|
|
538
|
+
try {
|
|
539
|
+
res = await fetch(url, {
|
|
540
|
+
method,
|
|
541
|
+
headers: {
|
|
542
|
+
'Content-Type': 'application/json',
|
|
543
|
+
'Authorization': `Bearer ${MACHINE_API_KEY}`,
|
|
544
|
+
},
|
|
545
|
+
body: body != null ? JSON.stringify(body) : undefined,
|
|
546
|
+
});
|
|
547
|
+
} catch (error) {
|
|
548
|
+
throw buildDirectApiTransportError({ method, apiPath, error });
|
|
549
|
+
}
|
|
539
550
|
if (!res.ok) {
|
|
540
551
|
const text = await res.text();
|
|
541
|
-
throw
|
|
552
|
+
throw buildDirectApiHttpError({ method, apiPath, status: res.status, text });
|
|
542
553
|
}
|
|
543
554
|
return res.json();
|
|
544
555
|
}
|
|
@@ -555,16 +566,21 @@ async function directApiVideoUpload(apiPath, {
|
|
|
555
566
|
};
|
|
556
567
|
if (filename) headers['X-File-Name'] = filename;
|
|
557
568
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
569
|
+
let res;
|
|
570
|
+
try {
|
|
571
|
+
res = await fetch(url, {
|
|
572
|
+
method: 'POST',
|
|
573
|
+
headers,
|
|
574
|
+
body: createReadStream(localPath),
|
|
575
|
+
duplex: 'half',
|
|
576
|
+
});
|
|
577
|
+
} catch (error) {
|
|
578
|
+
throw buildDirectApiTransportError({ method: 'POST', apiPath, error });
|
|
579
|
+
}
|
|
564
580
|
|
|
565
581
|
if (!res.ok) {
|
|
566
582
|
const text = await res.text();
|
|
567
|
-
throw
|
|
583
|
+
throw buildDirectApiHttpError({ method: 'POST', apiPath, status: res.status, text });
|
|
568
584
|
}
|
|
569
585
|
return res.json();
|
|
570
586
|
}
|
|
@@ -701,12 +717,14 @@ async function runMandatoryLocalTool({ toolName, toolInput = {}, executor }) {
|
|
|
701
717
|
});
|
|
702
718
|
return result;
|
|
703
719
|
} catch (error) {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
720
|
+
if (shouldEmitToolCallFailed(error)) {
|
|
721
|
+
enqueueBundleEvent('tool_call_failed', {
|
|
722
|
+
trace_id: traceId,
|
|
723
|
+
tool_name: toolName,
|
|
724
|
+
tool_classification: classification,
|
|
725
|
+
reason: toolCallFailedReason(error),
|
|
726
|
+
});
|
|
727
|
+
}
|
|
710
728
|
throw error;
|
|
711
729
|
}
|
|
712
730
|
}
|
|
@@ -854,12 +872,14 @@ async function api(method, apiPath, body) {
|
|
|
854
872
|
});
|
|
855
873
|
return data;
|
|
856
874
|
} catch (err) {
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
875
|
+
if (shouldEmitToolCallFailed(err)) {
|
|
876
|
+
enqueueBundleEvent('tool_call_failed', {
|
|
877
|
+
trace_id: traceId,
|
|
878
|
+
tool_name: toolName,
|
|
879
|
+
tool_classification: classification,
|
|
880
|
+
reason: toolCallFailedReason(err),
|
|
881
|
+
});
|
|
882
|
+
}
|
|
863
883
|
throw err;
|
|
864
884
|
}
|
|
865
885
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function buildDirectApiTransportError({ method, apiPath, error }) {
|
|
2
|
+
const wrapped = new Error(`API ${method} ${apiPath} transport_error: ${error?.message ?? 'unknown_error'}`);
|
|
3
|
+
wrapped.code = error?.name === 'AbortError' ? 'timeout' : 'server_error';
|
|
4
|
+
wrapped.cause = error;
|
|
5
|
+
return wrapped;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function buildDirectApiHttpError({ method, apiPath, status, text }) {
|
|
9
|
+
const error = new Error(`API ${method} ${apiPath} → ${status}: ${text}`);
|
|
10
|
+
error.status = status;
|
|
11
|
+
error.code = status >= 500 ? 'server_error' : 'api_client_error';
|
|
12
|
+
return error;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function shouldEmitToolCallFailed(error) {
|
|
16
|
+
const status = Number(error?.status);
|
|
17
|
+
if (Number.isFinite(status) && status >= 400 && status < 500) return false;
|
|
18
|
+
if (error?.code === 'api_client_error') return false;
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function toolCallFailedReason(error) {
|
|
23
|
+
if (typeof error?.code === 'string' && error.code.trim()) return error.code.trim();
|
|
24
|
+
if (typeof error?.message === 'string' && error.message.trim()) return error.message.trim();
|
|
25
|
+
return 'unknown_error';
|
|
26
|
+
}
|