@11agents/cli 0.1.33 → 0.1.35
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.
|
@@ -326,6 +326,16 @@ class XiaohongshuAdbPublisher:
|
|
|
326
326
|
expected_title=title,
|
|
327
327
|
expected_caption=composed_caption,
|
|
328
328
|
)
|
|
329
|
+
if link_result.status != "retrieved" or not link_result.platform_permalink:
|
|
330
|
+
link_shot = (
|
|
331
|
+
Path(link_result.screenshot_path)
|
|
332
|
+
if link_result.screenshot_path
|
|
333
|
+
else run_dir / "link" / "failed-copy-link.png"
|
|
334
|
+
)
|
|
335
|
+
raise XiaohongshuAutomationError(
|
|
336
|
+
f"Xiaohongshu post-publish link recovery failed: {link_result.error or link_result.status}",
|
|
337
|
+
link_shot,
|
|
338
|
+
)
|
|
329
339
|
|
|
330
340
|
ended_epoch = int(time.time())
|
|
331
341
|
if status == "published":
|
|
@@ -381,6 +391,10 @@ class XiaohongshuAdbPublisher:
|
|
|
381
391
|
remote_media_path=remote,
|
|
382
392
|
screenshot_path=str(human_shot),
|
|
383
393
|
error=str(exc),
|
|
394
|
+
platform_permalink=link_result.platform_permalink if link_result else "",
|
|
395
|
+
link_status=link_result.status if link_result else "",
|
|
396
|
+
link_error=link_result.error if link_result else "",
|
|
397
|
+
link_screenshot_path=link_result.screenshot_path if link_result else "",
|
|
384
398
|
verification_status="failed",
|
|
385
399
|
verification_error=str(exc),
|
|
386
400
|
profile_screenshot_path=str(run_dir / "profile.png") if (run_dir / "profile.png").exists() else "",
|
|
@@ -401,6 +415,10 @@ class XiaohongshuAdbPublisher:
|
|
|
401
415
|
remote_media_path=remote,
|
|
402
416
|
screenshot_path=str(failure_shot),
|
|
403
417
|
error=str(exc),
|
|
418
|
+
platform_permalink=link_result.platform_permalink if link_result else "",
|
|
419
|
+
link_status=link_result.status if link_result else "",
|
|
420
|
+
link_error=link_result.error if link_result else "",
|
|
421
|
+
link_screenshot_path=link_result.screenshot_path if link_result else "",
|
|
404
422
|
)
|
|
405
423
|
|
|
406
424
|
def copy_current_note_link(
|
package/package.json
CHANGED
package/src/commands/runtime.js
CHANGED
|
@@ -874,6 +874,99 @@ function normalizeSkillBundle(skill = {}) {
|
|
|
874
874
|
}
|
|
875
875
|
}
|
|
876
876
|
|
|
877
|
+
async function callProjectMcp(toolName, toolArgs, syncConfig, deps) {
|
|
878
|
+
const result = await deps.requestJson('/mcp', {
|
|
879
|
+
method: 'POST',
|
|
880
|
+
body: {
|
|
881
|
+
jsonrpc: '2.0',
|
|
882
|
+
id: 1,
|
|
883
|
+
method: 'tools/call',
|
|
884
|
+
params: { name: toolName, arguments: toolArgs },
|
|
885
|
+
},
|
|
886
|
+
config: syncConfig,
|
|
887
|
+
})
|
|
888
|
+
if (result.error) throw new Error(result.error.message || `MCP ${toolName} failed`)
|
|
889
|
+
const text = result.result?.content?.[0]?.text || '{}'
|
|
890
|
+
return JSON.parse(text)
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
async function syncSkillsViaMcp({ task, workdir, flags, deps, projectToken, config }) {
|
|
894
|
+
const agentId = task.agent?.id
|
|
895
|
+
if (!agentId) return { changed: false, count: 0 }
|
|
896
|
+
|
|
897
|
+
const syncConfig = { ...config, token: projectToken }
|
|
898
|
+
const skillsDir = path.join(workdir, 'agents', slugify(agentNameForTask(task), 'agent'), 'skills')
|
|
899
|
+
|
|
900
|
+
const manifest = await callProjectMcp('skill_list', { agent_id: agentId }, syncConfig, deps)
|
|
901
|
+
const cloudSkills = Array.isArray(manifest.skills) ? manifest.skills : []
|
|
902
|
+
|
|
903
|
+
await mkdir(skillsDir, { recursive: true })
|
|
904
|
+
let changed = false
|
|
905
|
+
|
|
906
|
+
// Remove local skill dirs that no longer exist in the cloud
|
|
907
|
+
const cloudSkillSlugs = new Set(cloudSkills.map(s => slugify(s.name, 'skill')))
|
|
908
|
+
const localDirs = await readdir(skillsDir, { withFileTypes: true }).catch(() => [])
|
|
909
|
+
for (const entry of localDirs) {
|
|
910
|
+
if (entry.isDirectory() && !cloudSkillSlugs.has(entry.name)) {
|
|
911
|
+
await rm(path.join(skillsDir, entry.name), { recursive: true, force: true })
|
|
912
|
+
changed = true
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
for (const cloudSkill of cloudSkills) {
|
|
917
|
+
const skillSlug = slugify(cloudSkill.name, 'skill')
|
|
918
|
+
const skillDir = path.join(skillsDir, skillSlug)
|
|
919
|
+
const cloudFiles = Array.isArray(cloudSkill.files) ? cloudSkill.files : []
|
|
920
|
+
|
|
921
|
+
// Check for any missing local file
|
|
922
|
+
const presentFlags = await Promise.all(
|
|
923
|
+
cloudFiles.map(f => readFile(path.join(skillDir, assertSafeRelativePath(f.name))).then(() => true).catch(() => false))
|
|
924
|
+
)
|
|
925
|
+
const hasMissing = presentFlags.some(p => !p)
|
|
926
|
+
|
|
927
|
+
if (hasMissing) {
|
|
928
|
+
// Full re-fetch: delete skill dir and pull every file fresh
|
|
929
|
+
await rm(skillDir, { recursive: true, force: true })
|
|
930
|
+
await mkdir(skillDir, { recursive: true })
|
|
931
|
+
for (const cloudFile of cloudFiles) {
|
|
932
|
+
const fetched = await callProjectMcp('skill_file_get', {
|
|
933
|
+
agent_id: agentId,
|
|
934
|
+
skill_name: cloudSkill.name,
|
|
935
|
+
file_path: cloudFile.name,
|
|
936
|
+
}, syncConfig, deps)
|
|
937
|
+
const target = path.join(skillDir, assertSafeRelativePath(cloudFile.name))
|
|
938
|
+
await mkdir(path.dirname(target), { recursive: true })
|
|
939
|
+
const content = fetched.encoding === 'base64'
|
|
940
|
+
? Buffer.from(fetched.content || '', 'base64')
|
|
941
|
+
: String(fetched.content || '')
|
|
942
|
+
await writeFile(target, content)
|
|
943
|
+
}
|
|
944
|
+
changed = true
|
|
945
|
+
} else {
|
|
946
|
+
// All files present — diff by MD5
|
|
947
|
+
for (const cloudFile of cloudFiles) {
|
|
948
|
+
const localPath = path.join(skillDir, assertSafeRelativePath(cloudFile.name))
|
|
949
|
+
const localBuf = await readFile(localPath)
|
|
950
|
+
const localMd5 = createHash('md5').update(localBuf).digest('hex')
|
|
951
|
+
if (localMd5 !== cloudFile.md5) {
|
|
952
|
+
const fetched = await callProjectMcp('skill_file_get', {
|
|
953
|
+
agent_id: agentId,
|
|
954
|
+
skill_name: cloudSkill.name,
|
|
955
|
+
file_path: cloudFile.name,
|
|
956
|
+
}, syncConfig, deps)
|
|
957
|
+
const content = fetched.encoding === 'base64'
|
|
958
|
+
? Buffer.from(fetched.content || '', 'base64')
|
|
959
|
+
: String(fetched.content || '')
|
|
960
|
+
await writeFile(localPath, content)
|
|
961
|
+
changed = true
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
return { changed, count: cloudSkills.length, skills_dir: skillsDir }
|
|
968
|
+
}
|
|
969
|
+
|
|
877
970
|
async function materializeSkillsIfChanged({ task, workdir, flags, deps }) {
|
|
878
971
|
const skills = Array.isArray(task.agent?.skills) ? task.agent.skills.map(normalizeSkillBundle) : []
|
|
879
972
|
if (!skills.length) return { changed: false, count: 0 }
|
|
@@ -1016,11 +1109,10 @@ async function prepareRuntimeTask(task, flags, deps, config) {
|
|
|
1016
1109
|
await mkdir(agentMemoryDir, { recursive: true })
|
|
1017
1110
|
|
|
1018
1111
|
const database = await syncDatabaseIfNeeded({ task, workdir, config, flags, deps })
|
|
1019
|
-
const skills = await materializeSkillsIfChanged({ task, workdir, flags, deps })
|
|
1020
1112
|
|
|
1021
|
-
// Resolve
|
|
1022
|
-
// runtime agent can authenticate without needing credentials on disk.
|
|
1113
|
+
// Resolve project token before skill sync so MCP auth is available.
|
|
1023
1114
|
const projectToken = await projectSyncToken(projectTokenCandidatesForTask(task, flags), flags, deps, task.workspace?.swarm_token)
|
|
1115
|
+
const skills = await syncSkillsViaMcp({ task, workdir, flags, deps, projectToken, config })
|
|
1024
1116
|
const env = {
|
|
1025
1117
|
...process.env,
|
|
1026
1118
|
...agentEnvironment(task),
|