@lightcone-ai/daemon 0.9.78 → 0.9.79
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,19 +34,42 @@ const REQUIREMENTS = {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
const CREATOR_HOME_URL = 'https://creator.douyin.com/creator-micro/home';
|
|
37
|
-
const
|
|
38
|
-
const
|
|
37
|
+
const UPLOAD_URL = 'https://creator.douyin.com/creator-micro/content/upload';
|
|
38
|
+
const IMAGE_PUBLISH_URL = `${UPLOAD_URL}?default-tab=3&enter_from=publish`;
|
|
39
|
+
const VIDEO_PUBLISH_URL = `${UPLOAD_URL}?default-tab=1&enter_from=publish`;
|
|
39
40
|
|
|
40
|
-
const
|
|
41
|
+
const IMAGE_FILE_INPUT_SELECTOR = [
|
|
41
42
|
'input[type="file"][accept*="image"]',
|
|
42
43
|
'input[type="file"][accept*=".jpg"]',
|
|
43
44
|
'input[type="file"][accept*=".jpeg"]',
|
|
44
45
|
'input[type="file"][accept*=".png"]',
|
|
45
46
|
'input[type="file"][accept*=".webp"]',
|
|
47
|
+
'input[type="file"][accept="image"]',
|
|
46
48
|
'input[type="file"]',
|
|
47
49
|
].join(', ');
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
+
const VIDEO_FILE_INPUT_SELECTOR = [
|
|
51
|
+
'input[type="file"][accept*="video"]',
|
|
52
|
+
'input[type="file"][accept*=".mp4"]',
|
|
53
|
+
'input[type="file"][accept*=".mov"]',
|
|
54
|
+
'input[type="file"][accept*=".webm"]',
|
|
55
|
+
'input[type="file"]',
|
|
56
|
+
].join(', ');
|
|
57
|
+
const FILE_INPUT_SELECTOR = `${IMAGE_FILE_INPUT_SELECTOR}, ${VIDEO_FILE_INPUT_SELECTOR}`;
|
|
58
|
+
const TITLE_SELECTOR = [
|
|
59
|
+
'input[placeholder*="标题"]',
|
|
60
|
+
'textarea[placeholder*="标题"]',
|
|
61
|
+
'[contenteditable="true"][data-placeholder*="标题"]',
|
|
62
|
+
'[contenteditable="true"][aria-label*="标题"]',
|
|
63
|
+
].join(', ');
|
|
64
|
+
const CONTENT_SELECTOR = [
|
|
65
|
+
'textarea[placeholder*="描述"]',
|
|
66
|
+
'textarea[placeholder*="作品描述"]',
|
|
67
|
+
'[placeholder*="添加作品描述"]',
|
|
68
|
+
'[contenteditable="true"][data-placeholder*="描述"]',
|
|
69
|
+
'[contenteditable="true"][aria-label*="描述"]',
|
|
70
|
+
'.ProseMirror[contenteditable="true"]',
|
|
71
|
+
'[contenteditable="true"]',
|
|
72
|
+
].join(', ');
|
|
50
73
|
|
|
51
74
|
const ERROR_SELECTORS = [
|
|
52
75
|
'[class*="error"]',
|
|
@@ -88,16 +111,16 @@ export class DouyinAdapter {
|
|
|
88
111
|
if (images.length > 0) {
|
|
89
112
|
await this._ensureUploaderReady('image');
|
|
90
113
|
await humanPause(900, 2200, 'before-upload');
|
|
91
|
-
await this._uploadFiles(images);
|
|
114
|
+
await this._uploadFiles(images, 'image');
|
|
92
115
|
await this._waitForUploadSettled(images.length, 120_000);
|
|
93
116
|
await humanPause(2500, 5500, 'after-upload');
|
|
94
117
|
}
|
|
95
118
|
|
|
96
119
|
const fullText = formatTextWithTags(text, tags);
|
|
97
|
-
await this._fillField(CONTENT_SELECTOR, fullText);
|
|
120
|
+
await this._fillField(CONTENT_SELECTOR, fullText, 'content');
|
|
98
121
|
|
|
99
122
|
if (title) {
|
|
100
|
-
await this._fillField(TITLE_SELECTOR, title);
|
|
123
|
+
await this._fillField(TITLE_SELECTOR, title, 'title');
|
|
101
124
|
}
|
|
102
125
|
|
|
103
126
|
await humanPause(4500, 9000, 'before-publish-check');
|
|
@@ -123,7 +146,7 @@ export class DouyinAdapter {
|
|
|
123
146
|
|
|
124
147
|
await this._ensureUploaderReady('video');
|
|
125
148
|
await humanPause(900, 2200, 'before-upload');
|
|
126
|
-
await this._uploadFiles([video]);
|
|
149
|
+
await this._uploadFiles([video], 'video');
|
|
127
150
|
await this._waitForUploadSettled(1, 180_000);
|
|
128
151
|
await humanPause(3000, 6500, 'after-video-upload');
|
|
129
152
|
|
|
@@ -132,10 +155,10 @@ export class DouyinAdapter {
|
|
|
132
155
|
}
|
|
133
156
|
|
|
134
157
|
const fullText = formatTextWithTags(text, tags);
|
|
135
|
-
await this._fillField(CONTENT_SELECTOR, fullText);
|
|
158
|
+
await this._fillField(CONTENT_SELECTOR, fullText, 'content');
|
|
136
159
|
|
|
137
160
|
if (title) {
|
|
138
|
-
await this._fillField(TITLE_SELECTOR, title);
|
|
161
|
+
await this._fillField(TITLE_SELECTOR, title, 'title');
|
|
139
162
|
}
|
|
140
163
|
|
|
141
164
|
await humanPause(4500, 9000, 'before-publish-check');
|
|
@@ -216,9 +239,10 @@ export class DouyinAdapter {
|
|
|
216
239
|
}
|
|
217
240
|
|
|
218
241
|
async _ensureUploaderReady(kind) {
|
|
219
|
-
|
|
242
|
+
const selector = kind === 'video' ? VIDEO_FILE_INPUT_SELECTOR : IMAGE_FILE_INPUT_SELECTOR;
|
|
243
|
+
if (await this._waitForSelectorQuiet(selector, 8000)) return;
|
|
220
244
|
await this._ensureComposer(kind);
|
|
221
|
-
if (await this._waitForSelectorQuiet(
|
|
245
|
+
if (await this._waitForSelectorQuiet(selector, 12_000)) return;
|
|
222
246
|
|
|
223
247
|
const labels = kind === 'video'
|
|
224
248
|
? ['上传视频', '点击上传', '选择视频', '上传']
|
|
@@ -226,7 +250,7 @@ export class DouyinAdapter {
|
|
|
226
250
|
for (const label of labels) {
|
|
227
251
|
const clicked = await this._clickByText(label);
|
|
228
252
|
if (!clicked) continue;
|
|
229
|
-
if (await this._waitForSelectorQuiet(
|
|
253
|
+
if (await this._waitForSelectorQuiet(selector, 8000)) return;
|
|
230
254
|
}
|
|
231
255
|
|
|
232
256
|
const state = await this._inspectPage();
|
|
@@ -273,7 +297,7 @@ export class DouyinAdapter {
|
|
|
273
297
|
.map(t => t.trim())
|
|
274
298
|
.filter(Boolean)
|
|
275
299
|
.slice(0, 5);
|
|
276
|
-
const buttons = [...document.querySelectorAll('button, [role="button"]')].map(e => ({
|
|
300
|
+
const buttons = [...document.querySelectorAll('button, [role="button"], .semi-button, [class*="button"], [class*="Button"]')].map(e => ({
|
|
277
301
|
text: (e.innerText || e.textContent || '').trim(),
|
|
278
302
|
disabled: !!e.disabled || e.getAttribute('aria-disabled') === 'true' || e.className?.toString().includes('disabled'),
|
|
279
303
|
})).filter(b => b.text).slice(0, 30);
|
|
@@ -388,12 +412,58 @@ export class DouyinAdapter {
|
|
|
388
412
|
throw new Error(`PUBLISH_TIMEOUT: ${hint}`);
|
|
389
413
|
}
|
|
390
414
|
|
|
391
|
-
async _fillField(selector, value) {
|
|
415
|
+
async _fillField(selector, value, kind = 'content') {
|
|
392
416
|
await humanPause(500, 1400, 'before-focus-field');
|
|
393
417
|
const result = await this._cdp.send('Runtime.evaluate', {
|
|
394
418
|
expression: `
|
|
395
419
|
(function() {
|
|
396
|
-
const
|
|
420
|
+
const selector = ${JSON.stringify(selector)};
|
|
421
|
+
const kind = ${JSON.stringify(kind)};
|
|
422
|
+
const visible = (el) => {
|
|
423
|
+
if (!el) return false;
|
|
424
|
+
const r = el.getBoundingClientRect();
|
|
425
|
+
const s = getComputedStyle(el);
|
|
426
|
+
return r.width > 0 && r.height > 0 && r.left > -1000 && s.display !== 'none' && s.visibility !== 'hidden';
|
|
427
|
+
};
|
|
428
|
+
const metaText = (el) => [
|
|
429
|
+
el.getAttribute('placeholder'),
|
|
430
|
+
el.getAttribute('data-placeholder'),
|
|
431
|
+
el.getAttribute('aria-label'),
|
|
432
|
+
el.getAttribute('title'),
|
|
433
|
+
el.className?.toString?.(),
|
|
434
|
+
el.closest('[class*="editor"], [class*="form"], [class*="field"], [class*="mention"], [class*="caption"]')?.innerText,
|
|
435
|
+
].filter(Boolean).join(' ');
|
|
436
|
+
const candidates = [
|
|
437
|
+
...document.querySelectorAll('input, textarea, [contenteditable="true"]')
|
|
438
|
+
].filter(visible).filter(el => !el.disabled && el.getAttribute('aria-disabled') !== 'true');
|
|
439
|
+
|
|
440
|
+
function score(el) {
|
|
441
|
+
const text = metaText(el);
|
|
442
|
+
let s = 0;
|
|
443
|
+
if (kind === 'title') {
|
|
444
|
+
if (/标题|title|caption/i.test(text)) s += 120;
|
|
445
|
+
if (/添加作品标题|图文标题|作品标题/.test(text)) s += 80;
|
|
446
|
+
if (/描述|正文|description/i.test(text)) s -= 120;
|
|
447
|
+
if (el.tagName === 'INPUT') s += 20;
|
|
448
|
+
} else {
|
|
449
|
+
if (/描述|作品描述|添加作品描述|正文|description/i.test(text)) s += 120;
|
|
450
|
+
if (/标题|title|caption/i.test(text)) s -= 100;
|
|
451
|
+
if (el.tagName === 'TEXTAREA') s += 30;
|
|
452
|
+
if (el.getAttribute('contenteditable') === 'true') s += 15;
|
|
453
|
+
}
|
|
454
|
+
const r = el.getBoundingClientRect();
|
|
455
|
+
if (r.width < 40 || r.height < 12) s -= 40;
|
|
456
|
+
return s;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
let el = null;
|
|
460
|
+
const direct = [...document.querySelectorAll(selector)].filter(visible);
|
|
461
|
+
if (direct.length) {
|
|
462
|
+
el = direct.sort((a, b) => score(b) - score(a))[0];
|
|
463
|
+
}
|
|
464
|
+
if (!el || score(el) <= 0) {
|
|
465
|
+
el = candidates.sort((a, b) => score(b) - score(a))[0];
|
|
466
|
+
}
|
|
397
467
|
if (!el) return false;
|
|
398
468
|
el.focus();
|
|
399
469
|
if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
|
|
@@ -417,7 +487,7 @@ export class DouyinAdapter {
|
|
|
417
487
|
`,
|
|
418
488
|
returnByValue: true,
|
|
419
489
|
});
|
|
420
|
-
if (result.result?.value !== true) throw new Error(`PUBLISH_FAILED:
|
|
490
|
+
if (result.result?.value !== true) throw new Error(`PUBLISH_FAILED: 找不到${kind === 'title' ? '标题' : '描述'}输入框:${selector}`);
|
|
421
491
|
await this._typeTextHumanLike(value);
|
|
422
492
|
await this._cdp.send('Runtime.evaluate', {
|
|
423
493
|
expression: `
|
|
@@ -446,10 +516,32 @@ export class DouyinAdapter {
|
|
|
446
516
|
}
|
|
447
517
|
}
|
|
448
518
|
|
|
449
|
-
async _uploadFiles(filePaths) {
|
|
519
|
+
async _uploadFiles(filePaths, kind = 'image') {
|
|
450
520
|
await humanPause(600, 1700, 'before-set-files');
|
|
451
521
|
const result = await this._cdp.send('Runtime.evaluate', {
|
|
452
|
-
expression: `
|
|
522
|
+
expression: `
|
|
523
|
+
(function() {
|
|
524
|
+
const kind = ${JSON.stringify(kind)};
|
|
525
|
+
const inputs = [...document.querySelectorAll('input[type="file"]')];
|
|
526
|
+
if (!inputs.length) return null;
|
|
527
|
+
const score = (el) => {
|
|
528
|
+
const accept = (el.getAttribute('accept') || '').toLowerCase();
|
|
529
|
+
let s = 0;
|
|
530
|
+
if (kind === 'image') {
|
|
531
|
+
if (/image|jpg|jpeg|png|webp/.test(accept)) s += 100;
|
|
532
|
+
if (/video|mp4|mov|webm/.test(accept)) s -= 100;
|
|
533
|
+
} else {
|
|
534
|
+
if (/video|mp4|mov|webm/.test(accept)) s += 100;
|
|
535
|
+
if (/image|jpg|jpeg|png|webp/.test(accept)) s -= 100;
|
|
536
|
+
}
|
|
537
|
+
const containerText = (el.closest('div')?.innerText || '').slice(0, 200);
|
|
538
|
+
if (kind === 'image' && /图文|图片/.test(containerText)) s += 20;
|
|
539
|
+
if (kind === 'video' && /视频/.test(containerText)) s += 20;
|
|
540
|
+
return s;
|
|
541
|
+
};
|
|
542
|
+
return inputs.sort((a, b) => score(b) - score(a))[0] ?? null;
|
|
543
|
+
})()
|
|
544
|
+
`,
|
|
453
545
|
returnByValue: false,
|
|
454
546
|
});
|
|
455
547
|
if (!result.result?.objectId) throw new Error('No file input found on page');
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
13
13
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
14
14
|
import { z } from 'zod';
|
|
15
|
-
import { existsSync, statSync, realpathSync } from 'fs';
|
|
15
|
+
import { existsSync, statSync, realpathSync, mkdirSync, writeFileSync } from 'fs';
|
|
16
16
|
import path from 'path';
|
|
17
17
|
import { getSession, closeSession } from './chrome-pool.js';
|
|
18
18
|
import { XhsAdapter } from './adapters/xhs.js';
|
|
@@ -23,7 +23,9 @@ import { withProfileLock } from '../../src/profile-lock.js';
|
|
|
23
23
|
const SERVER_URL = process.env.SERVER_URL ?? '';
|
|
24
24
|
const MACHINE_API_KEY = process.env.MACHINE_API_KEY ?? '';
|
|
25
25
|
const AGENT_ID = process.env.AGENT_ID ?? '';
|
|
26
|
+
const TEAM_ID = process.env.TEAM_ID ?? '';
|
|
26
27
|
const WORKSPACE_DIR = process.env.WORKSPACE_DIR ?? process.cwd();
|
|
28
|
+
const TEAM_WORKSPACE_DIR = path.dirname(WORKSPACE_DIR);
|
|
27
29
|
|
|
28
30
|
// ── Platform registry ──────────────────────────────────────────────────────────
|
|
29
31
|
|
|
@@ -115,6 +117,74 @@ const MEDIA_LIMITS = {
|
|
|
115
117
|
kuaishou: { maxImages: 12, imageExts: ['.jpg', '.jpeg', '.png'], videoExts: ['.mp4', '.mov'] },
|
|
116
118
|
};
|
|
117
119
|
|
|
120
|
+
function isInsideDir(filePath, dir) {
|
|
121
|
+
const rel = path.relative(dir, filePath);
|
|
122
|
+
return rel === '' || (!!rel && !rel.startsWith('..') && !path.isAbsolute(rel));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function decodeWorkspaceContent(content) {
|
|
126
|
+
if (typeof content !== 'string') throw new Error('workspace file content is not a string');
|
|
127
|
+
if (!content.startsWith('data:')) return Buffer.from(content, 'utf8');
|
|
128
|
+
|
|
129
|
+
const commaIdx = content.indexOf(',');
|
|
130
|
+
if (commaIdx === -1) throw new Error('invalid data URL in workspace file');
|
|
131
|
+
const header = content.slice(5, commaIdx);
|
|
132
|
+
const body = content.slice(commaIdx + 1);
|
|
133
|
+
if (header.split(';').includes('base64')) return Buffer.from(body, 'base64');
|
|
134
|
+
return Buffer.from(decodeURIComponent(body), 'utf8');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function workspacePathFromMediaPath(filePath, approvalData) {
|
|
138
|
+
if (!filePath) return null;
|
|
139
|
+
|
|
140
|
+
const normalized = filePath.replaceAll('\\', '/');
|
|
141
|
+
const virtualMatch = normalized.match(/^\/agent-workspace\/([^/]+)\/workspace\/(.+)$/);
|
|
142
|
+
if (virtualMatch) return { teamId: virtualMatch[1], relPath: virtualMatch[2] };
|
|
143
|
+
|
|
144
|
+
const workspaceSegmentMatch = normalized.match(/\/workspace\/((?:artifacts|notes|tmp)\/.+)$/);
|
|
145
|
+
if (workspaceSegmentMatch) {
|
|
146
|
+
return { teamId: approvalData?.teamId ?? TEAM_ID, relPath: workspaceSegmentMatch[1] };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!path.isAbsolute(filePath) && /^(artifacts|notes|tmp)\//.test(normalized)) {
|
|
150
|
+
return { teamId: approvalData?.teamId ?? TEAM_ID, relPath: normalized };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function materializeWorkspaceMedia(filePath, approvalData) {
|
|
157
|
+
if (!filePath || existsSync(filePath)) return filePath;
|
|
158
|
+
|
|
159
|
+
const workspacePath = workspacePathFromMediaPath(filePath, approvalData);
|
|
160
|
+
if (!workspacePath?.teamId || !workspacePath.relPath) return filePath;
|
|
161
|
+
|
|
162
|
+
const localPath = path.resolve(TEAM_WORKSPACE_DIR, workspacePath.relPath);
|
|
163
|
+
const allowedRoots = ['artifacts', 'notes', 'tmp'].map(dir => path.join(TEAM_WORKSPACE_DIR, dir));
|
|
164
|
+
if (!allowedRoots.some(root => isInsideDir(localPath, root))) {
|
|
165
|
+
throw new Error(`workspace media path is outside allowed team workspace directories: ${filePath}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (existsSync(localPath)) return localPath;
|
|
169
|
+
|
|
170
|
+
const data = await api(
|
|
171
|
+
'GET',
|
|
172
|
+
`/team-memory?path=${encodeURIComponent(workspacePath.relPath)}&teamId=${encodeURIComponent(workspacePath.teamId)}`
|
|
173
|
+
);
|
|
174
|
+
mkdirSync(path.dirname(localPath), { recursive: true });
|
|
175
|
+
writeFileSync(localPath, decodeWorkspaceContent(data.content));
|
|
176
|
+
console.error(`[publisher] Materialized team workspace media ${workspacePath.relPath} -> ${localPath}`);
|
|
177
|
+
return localPath;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function materializeMedia({ images = [], video, cover }, approvalData) {
|
|
181
|
+
return {
|
|
182
|
+
images: await Promise.all(images.map(filePath => materializeWorkspaceMedia(filePath, approvalData))),
|
|
183
|
+
video: video ? await materializeWorkspaceMedia(video, approvalData) : video,
|
|
184
|
+
cover: cover ? await materializeWorkspaceMedia(cover, approvalData) : cover,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
118
188
|
function validateLocalFile(filePath, { kind, required = false, allowedExts = [] }) {
|
|
119
189
|
if (!filePath) {
|
|
120
190
|
if (required) throw new Error(`${kind} file path is required`);
|
|
@@ -128,10 +198,15 @@ function validateLocalFile(filePath, { kind, required = false, allowedExts = []
|
|
|
128
198
|
}
|
|
129
199
|
|
|
130
200
|
const realFile = realpathSync(filePath);
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
201
|
+
const allowedRoots = [
|
|
202
|
+
realpathSync(WORKSPACE_DIR),
|
|
203
|
+
...['artifacts', 'notes', 'tmp']
|
|
204
|
+
.map(dir => path.join(TEAM_WORKSPACE_DIR, dir))
|
|
205
|
+
.filter(existsSync)
|
|
206
|
+
.map(dir => realpathSync(dir)),
|
|
207
|
+
];
|
|
208
|
+
if (!allowedRoots.some(root => isInsideDir(realFile, root))) {
|
|
209
|
+
throw new Error(`${kind} file must be inside the agent workspace or team shared artifacts/notes/tmp directory: ${filePath}`);
|
|
135
210
|
}
|
|
136
211
|
|
|
137
212
|
const stat = statSync(realFile);
|
|
@@ -191,8 +266,9 @@ images/video 字段填写本地绝对路径(在 agent workspace 目录下)
|
|
|
191
266
|
async ({ platform, content_type, title, text, tags, images, video, cover, approval_action_id }) => {
|
|
192
267
|
const label = PLATFORM_LABELS[platform] ?? platform;
|
|
193
268
|
try {
|
|
194
|
-
await validateApproval(approval_action_id, platform);
|
|
195
|
-
const
|
|
269
|
+
const approvalData = await validateApproval(approval_action_id, platform);
|
|
270
|
+
const localMedia = await materializeMedia({ images: images ?? [], video, cover }, approvalData);
|
|
271
|
+
const media = validateMedia({ platform, contentType: content_type, ...localMedia });
|
|
196
272
|
const result = await withPublisherProfile(platform, async () => {
|
|
197
273
|
const adapter = await getAdapter(platform);
|
|
198
274
|
if (content_type === 'image_text') {
|
package/package.json
CHANGED
package/src/agent-manager.js
CHANGED
package/src/drivers/codex.js
CHANGED
package/src/drivers/kimi.js
CHANGED
package/src/mcp-config.js
CHANGED
|
@@ -18,7 +18,7 @@ function resolveSkillArg(arg, config) {
|
|
|
18
18
|
return arg;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function baseEnvForServer(serverKey, { serverUrl, authToken, agentId, workspaceDir }) {
|
|
21
|
+
function baseEnvForServer(serverKey, { serverUrl, authToken, agentId, teamId, workspaceDir }) {
|
|
22
22
|
if (serverKey === 'workspace-migrate') {
|
|
23
23
|
return { SERVER_URL: serverUrl, MACHINE_API_KEY: authToken, AGENT_ID: agentId };
|
|
24
24
|
}
|
|
@@ -27,6 +27,7 @@ function baseEnvForServer(serverKey, { serverUrl, authToken, agentId, workspaceD
|
|
|
27
27
|
SERVER_URL: serverUrl,
|
|
28
28
|
MACHINE_API_KEY: authToken,
|
|
29
29
|
AGENT_ID: agentId,
|
|
30
|
+
TEAM_ID: teamId ?? '',
|
|
30
31
|
WORKSPACE_DIR: workspaceDir,
|
|
31
32
|
};
|
|
32
33
|
}
|
|
@@ -38,6 +39,7 @@ export function buildSkillMcpServers({
|
|
|
38
39
|
credentialGrants,
|
|
39
40
|
config,
|
|
40
41
|
agentId,
|
|
42
|
+
teamId,
|
|
41
43
|
workspaceDir,
|
|
42
44
|
serverUrl,
|
|
43
45
|
authToken,
|
|
@@ -61,7 +63,7 @@ export function buildSkillMcpServers({
|
|
|
61
63
|
args: resolvedArgs,
|
|
62
64
|
env: {
|
|
63
65
|
...resolvedEnv,
|
|
64
|
-
...baseEnvForServer(mc.server, { serverUrl, authToken, agentId, workspaceDir }),
|
|
66
|
+
...baseEnvForServer(mc.server, { serverUrl, authToken, agentId, teamId, workspaceDir }),
|
|
65
67
|
},
|
|
66
68
|
};
|
|
67
69
|
}
|