@lightcone-ai/daemon 0.14.11 → 0.14.13

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.14.11",
3
+ "version": "0.14.13",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -7,6 +7,7 @@ import { KuaishouAdapter } from '../mcp-servers/publisher/adapters/kuaishou.js';
7
7
  import { callOfficialTool } from '../mcp-servers/publisher/official-tool-client.js';
8
8
  import { runPublishPrecheck } from '../mcp-servers/publisher/precheck.js';
9
9
  import { withProfileLock } from './profile-lock.js';
10
+ import { profileDir as getBrowserProfileDir } from './browser-login.js';
10
11
 
11
12
  const PLATFORM_ENV_KEYS = {
12
13
  xhs: 'XHS_PROFILE_DIR',
@@ -36,10 +37,53 @@ function asObject(value) {
36
37
  return {};
37
38
  }
38
39
 
39
- function getProfileDir(platform) {
40
- const key = PLATFORM_ENV_KEYS[platform];
41
- if (!key) return null;
42
- return process.env[key] ?? null;
40
+ export function resolveProfileDir(platform, { ownerId } = {}) {
41
+ const envKey = PLATFORM_ENV_KEYS[platform];
42
+ const envValue = normalizeText(envKey ? process.env[envKey] : null);
43
+ if (envValue) {
44
+ return {
45
+ profileDir: envValue,
46
+ source: 'env',
47
+ envKey,
48
+ ownerId: normalizeText(ownerId),
49
+ };
50
+ }
51
+
52
+ const normalizedOwnerId = normalizeText(ownerId);
53
+ if (normalizedOwnerId) {
54
+ return {
55
+ profileDir: getBrowserProfileDir(platform, normalizedOwnerId),
56
+ source: 'owner',
57
+ envKey,
58
+ ownerId: normalizedOwnerId,
59
+ };
60
+ }
61
+
62
+ return {
63
+ profileDir: null,
64
+ source: 'missing_owner',
65
+ envKey,
66
+ ownerId: null,
67
+ };
68
+ }
69
+
70
+ export function resolveExistingProfileDir(platform, { ownerId } = {}) {
71
+ const resolved = resolveProfileDir(platform, { ownerId });
72
+ if (!resolved.profileDir) {
73
+ throw new Error('publish:job missing owner_id; server must include it for cross-machine profile resolution');
74
+ }
75
+
76
+ if (!existsSync(resolved.profileDir)) {
77
+ if (resolved.source === 'env') {
78
+ throw new Error(`Profile dir from ${resolved.envKey} not found: ${resolved.profileDir}`);
79
+ }
80
+ throw new Error(
81
+ `Profile dir for ${platform} not found at ${resolved.profileDir}; ` +
82
+ 'user must complete browser-login on this machine first'
83
+ );
84
+ }
85
+
86
+ return resolved.profileDir;
43
87
  }
44
88
 
45
89
  function getAdapterClass(platform) {
@@ -53,22 +97,16 @@ function createStaticAdapter(platform) {
53
97
  return new AdapterClass(null);
54
98
  }
55
99
 
56
- async function getAdapter(platform) {
57
- const profileDir = getProfileDir(platform);
58
- if (!profileDir) {
59
- throw new Error(`No profile dir for platform="${platform}". Has the user logged in and authorized this machine?`);
60
- }
61
- const cdp = await getSession(platform, profileDir);
100
+ async function getAdapter(platform, { ownerId } = {}) {
101
+ const resolvedProfileDir = resolveExistingProfileDir(platform, { ownerId });
102
+ const cdp = await getSession(platform, resolvedProfileDir);
62
103
  const AdapterClass = getAdapterClass(platform);
63
104
  return new AdapterClass(cdp);
64
105
  }
65
106
 
66
- async function withPublisherProfile(platform, fn) {
67
- const profileDir = getProfileDir(platform);
68
- if (!profileDir) {
69
- throw new Error(`No profile dir for platform="${platform}". Has the user logged in and authorized this machine?`);
70
- }
71
- return withProfileLock(platform, profileDir, {
107
+ async function withPublisherProfile(platform, { ownerId } = {}, fn) {
108
+ const resolvedProfileDir = resolveExistingProfileDir(platform, { ownerId });
109
+ return withProfileLock(platform, resolvedProfileDir, {
72
110
  owner: `publisher:${platform}`,
73
111
  timeoutMs: 30_000,
74
112
  staleMs: 20 * 60 * 1000,
@@ -110,6 +148,31 @@ export async function fetchPublishJobWorkspaceFile({ serverUrl, machineApiKey, j
110
148
  return res.json();
111
149
  }
112
150
 
151
+ export async function streamPublishJobVideo({ serverUrl, machineApiKey, jobId, videoId, localPath }) {
152
+ const normalizedJobId = String(jobId ?? '').trim();
153
+ if (!normalizedJobId) {
154
+ throw new Error('publish job id is required to fetch video files');
155
+ }
156
+ const normalizedVideoId = String(videoId ?? '').trim();
157
+ if (!normalizedVideoId) {
158
+ throw new Error('video id is required to fetch video files');
159
+ }
160
+ const url = `${String(serverUrl).replace(/\/$/, '')}/internal/agent/publish-jobs/${encodeURIComponent(normalizedJobId)}/video-files/${encodeURIComponent(normalizedVideoId)}`;
161
+ const res = await fetch(url, {
162
+ headers: {
163
+ Authorization: `Bearer ${machineApiKey}`,
164
+ },
165
+ });
166
+ if (!res.ok) {
167
+ const text = await res.text().catch(() => '');
168
+ throw new Error(`publish-jobs video-files GET failed (${res.status}): ${text}`);
169
+ }
170
+ const data = Buffer.from(await res.arrayBuffer());
171
+ mkdirSync(path.dirname(localPath), { recursive: true });
172
+ writeFileSync(localPath, data);
173
+ return localPath;
174
+ }
175
+
113
176
  export function workspacePathFromMediaPath(filePath, workspaceId) {
114
177
  if (!filePath) return null;
115
178
 
@@ -144,6 +207,7 @@ export async function materializeWorkspaceMedia({
144
207
  machineApiKey,
145
208
  jobId,
146
209
  fetchWorkspaceFile = fetchPublishJobWorkspaceFile,
210
+ streamVideoFile = streamPublishJobVideo,
147
211
  }) {
148
212
  if (!filePath || existsSync(filePath)) return filePath;
149
213
 
@@ -164,8 +228,18 @@ export async function materializeWorkspaceMedia({
164
228
  jobId,
165
229
  relPath: workspacePath.relPath,
166
230
  });
167
- mkdirSync(path.dirname(localPath), { recursive: true });
168
- writeFileSync(localPath, decodeWorkspaceContent(data.content));
231
+ if (data?.videoId) {
232
+ await streamVideoFile({
233
+ serverUrl,
234
+ machineApiKey,
235
+ jobId,
236
+ videoId: data.videoId,
237
+ localPath,
238
+ });
239
+ } else {
240
+ mkdirSync(path.dirname(localPath), { recursive: true });
241
+ writeFileSync(localPath, decodeWorkspaceContent(data.content));
242
+ }
169
243
  return localPath;
170
244
  }
171
245
 
@@ -368,6 +442,7 @@ export async function runPublishJob({ serverUrl, machineApiKey, agentId, workspa
368
442
  video,
369
443
  cover,
370
444
  } = buildJobInput(job);
445
+ const ownerId = normalizeText(job?.owner_id ?? job?.ownerId);
371
446
 
372
447
  if (!platform) throw new Error('publish job missing platform');
373
448
  if (!contentType) throw new Error('publish job missing content_type');
@@ -428,8 +503,8 @@ export async function runPublishJob({ serverUrl, machineApiKey, agentId, workspa
428
503
  });
429
504
 
430
505
  try {
431
- const { publishResult, healthCheck } = await withPublisherProfile(platform, async () => {
432
- const adapter = await getAdapter(platform);
506
+ const { publishResult, healthCheck } = await withPublisherProfile(platform, { ownerId }, async () => {
507
+ const adapter = await getAdapter(platform, { ownerId });
433
508
  const prePublishLogin = await adapter.checkLoginStatus();
434
509
  if (prePublishLogin?.loggedIn === false) {
435
510
  throw new Error(`LOGIN_EXPIRED:${platform}`);