@11agents/cli 0.1.41 → 0.1.43

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": "@11agents/cli",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "description": "11agents local runtime and telemetry CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,4 +1,4 @@
1
- import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises'
1
+ import { mkdir, readFile, readdir, rm, rmdir, writeFile } from 'node:fs/promises'
2
2
  import os from 'node:os'
3
3
  import path from 'node:path'
4
4
  import { flag } from '../args.js'
@@ -116,6 +116,27 @@ export function buildKnowledgeBaseDir(project, { homeDir = os.homedir() } = {})
116
116
  return path.join(homeDir, '.11agents', slugify(project), 'knowledge_base')
117
117
  }
118
118
 
119
+ // Mirror the skill-sync strategy: after a sync the cloud snapshot is the source
120
+ // of truth, so local knowledge text files missing from it are removed. Scope is
121
+ // strictly the project's knowledge_base directory.
122
+ async function pruneLocalKnowledgeFiles(baseDir, keepPaths) {
123
+ for (const localPath of await listKnowledgeTextFiles(baseDir)) {
124
+ if (keepPaths.has(localPath)) continue
125
+ await rm(path.join(baseDir, localPath), { force: true })
126
+ }
127
+ const removeEmptyDirs = async dir => {
128
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => [])
129
+ for (const entry of entries) {
130
+ if (entry.isDirectory()) await removeEmptyDirs(path.join(dir, entry.name))
131
+ }
132
+ if (dir !== baseDir) {
133
+ const remaining = await readdir(dir).catch(() => ['keep'])
134
+ if (remaining.length === 0) await rmdir(dir).catch(() => {})
135
+ }
136
+ }
137
+ await removeEmptyDirs(baseDir)
138
+ }
139
+
119
140
  export async function writeKnowledgeSnapshot(project, snapshot, { homeDir = os.homedir() } = {}) {
120
141
  const normalized = normalizeSnapshot(snapshot)
121
142
  const baseDir = buildKnowledgeBaseDir(project, { homeDir })
@@ -138,6 +159,7 @@ export async function writeKnowledgeSnapshot(project, snapshot, { homeDir = os.h
138
159
  await mkdir(path.dirname(target), { recursive: true })
139
160
  await writeFile(target, markdownForNode(node))
140
161
  }
162
+ await pruneLocalKnowledgeFiles(baseDir, new Set(normalized.nodes.map(node => node.local_path)))
141
163
  return index
142
164
  }
143
165
 
@@ -65,8 +65,10 @@ const COMMAND_ALIASES = new Map([
65
65
 
66
66
  const WRAPPER_FLAG_NAMES = new Set([
67
67
  'mobile-home',
68
+ 'project',
68
69
  'task-id',
69
70
  'task-title',
71
+ 'workspace',
70
72
  ])
71
73
 
72
74
  const EMPTY_DATA_FILES = [
@@ -99,7 +101,7 @@ Runtime:
99
101
  Python/device-control code is bundled with @11agents/cli.
100
102
  Mutable runtime state lives in ~/.11agents/mobile by default.
101
103
  Every command writes logs under ~/.11agents/mobile/runs/<task_id>/log.
102
- Xiaohongshu/TikTok publish-success DingTalk notification is enabled by
104
+ X/Xiaohongshu/TikTok publish-success DingTalk notification is enabled by
103
105
  ELEVENAGENTS_PUBLISH_DINGTALK_WEBHOOK and optional
104
106
  ELEVENAGENTS_PUBLISH_DINGTALK_SECRET.`)
105
107
  }
@@ -602,10 +604,12 @@ function buildRunContext(rawArgs, deps = {}) {
602
604
  || env.ELEVENAGENTS_ISSUE_TITLE
603
605
  || taskId
604
606
  ).trim()
607
+ const project = projectFromMobileContext(flags, env)
605
608
  const runDir = path.join(home, 'runs', taskId)
606
609
  return {
607
610
  flags,
608
611
  home,
612
+ project,
609
613
  taskId,
610
614
  taskTitle,
611
615
  runDir,
@@ -613,6 +617,20 @@ function buildRunContext(rawArgs, deps = {}) {
613
617
  }
614
618
  }
615
619
 
620
+ function projectFromMobileContext(flags = {}, env = process.env) {
621
+ return String(
622
+ flag(flags, 'project')
623
+ || flag(flags, 'workspace')
624
+ || env.ELEVENAGENTS_PROJECT_SLUG
625
+ || env.ELEVENAGENTS_PROJECT
626
+ || env.ELEVENAGENTS_PROJECT_NAME
627
+ || env.ELEVENAGENTS_WORKSPACE_SLUG
628
+ || env.GTM_SWARM_WORKSPACE
629
+ || env.WORKSPACE_SLUG
630
+ || ''
631
+ ).trim()
632
+ }
633
+
616
634
  async function dispatchPython(rawArgs, deps = {}) {
617
635
  const command = normalizePythonCommand(rawArgs[0] || '')
618
636
  if (!PYTHON_COMMANDS.has(command)) throw new Error(`unsupported mobile command: ${rawArgs[0] || ''}`)
@@ -622,6 +640,7 @@ async function dispatchPython(rawArgs, deps = {}) {
622
640
  const env = pythonEnv(context.home, deps.env || process.env)
623
641
  env.ELEVENAGENTS_MOBILE_RUN_DIR = context.runDir
624
642
  env.ELEVENAGENTS_TASK_ID = context.taskId
643
+ if (context.project) env.ELEVENAGENTS_PROJECT_SLUG = context.project
625
644
  const result = await runAndLog({
626
645
  command: python,
627
646
  args: childArgs,
@@ -661,6 +680,7 @@ async function dispatchScript(rawArgs, deps = {}) {
661
680
  const env = pythonEnv(context.home, deps.env || process.env)
662
681
  env.ELEVENAGENTS_MOBILE_RUN_DIR = context.runDir
663
682
  env.ELEVENAGENTS_TASK_ID = context.taskId
683
+ if (context.project) env.ELEVENAGENTS_PROJECT_SLUG = context.project
664
684
  const result = await runAndLog({
665
685
  command: python,
666
686
  args: [scriptPath, ...stripWrapperFlags(rawArgs.slice(1))],
@@ -702,6 +722,7 @@ async function notifyMobilePublishSuccess({ command, context, parsed, env = proc
702
722
  const link = publishResultLink(row)
703
723
  const content = buildPublishNotificationContent({
704
724
  platform,
725
+ project: context.project,
705
726
  task: context.taskTitle || context.taskId,
706
727
  deviceId: row.device_id || row.device || '',
707
728
  link,
@@ -755,7 +776,7 @@ function isPublishedResult(row) {
755
776
  }
756
777
 
757
778
  function shouldNotifyPublishPlatform(platform) {
758
- return platform === 'xiaohongshu' || platform === 'tiktok'
779
+ return platform === 'xiaohongshu' || platform === 'tiktok' || platform === 'x'
759
780
  }
760
781
 
761
782
  function publishResultLink(row) {
@@ -768,13 +789,14 @@ function publishResultLink(row) {
768
789
  ).trim()
769
790
  }
770
791
 
771
- function buildPublishNotificationContent({ platform, task, deviceId = '', link }) {
792
+ function buildPublishNotificationContent({ platform, project = '', task, deviceId = '', link }) {
772
793
  const label = platformLabel(platform)
794
+ const projectText = String(project || '').trim()
773
795
  const taskText = String(task || 'task').trim()
774
796
  const deviceText = deviceId ? `(设备 ${deviceId})` : ''
775
- if (platform === 'tiktok') return `${label} ${taskText}${deviceText}已发布`
776
- const linkText = String(link || '').trim() || '未回收'
777
- return `${label} ${taskText}${deviceText}已发布,链接:${linkText}`
797
+ const linkText = String(link || '').trim() || '未收集到'
798
+ const prefix = projectText ? `[${projectText}] ` : ''
799
+ return `${prefix}${label} ${taskText}${deviceText}已发布,链接:${linkText}`
778
800
  }
779
801
 
780
802
  function platformLabel(platform) {
@@ -946,6 +968,7 @@ export const mobileInternals = {
946
968
  mobileHome,
947
969
  parseRawArgs,
948
970
  platformFromMobileCommand,
971
+ projectFromMobileContext,
949
972
  publishResultLink,
950
973
  shouldNotifyPublishPlatform,
951
974
  signedDingTalkUrl,
@@ -1124,10 +1124,13 @@ async function prepareRuntimeTask(task, flags, deps, config) {
1124
1124
  // Resolve project token before skill sync so MCP auth is available.
1125
1125
  const projectToken = await projectSyncToken(projectTokenCandidatesForTask(task, flags), flags, deps, task.workspace?.swarm_token)
1126
1126
  const skills = await syncSkillsViaMcp({ task, workdir, flags, deps, projectToken, config })
1127
+ const projectSlug = projectSlugForTask(task, flags)
1127
1128
  const env = {
1128
1129
  ...process.env,
1129
1130
  ...agentEnvironment(task),
1130
1131
  ELEVENAGENTS_PROJECT_DIR: workdir,
1132
+ ELEVENAGENTS_PROJECT_SLUG: projectSlug,
1133
+ ELEVENAGENTS_PROJECT_NAME: String(task.workspace?.name || task.workspace?.slug || task.workspace_slug || ''),
1131
1134
  ELEVENAGENTS_AGENT_DIR: agentDir,
1132
1135
  ELEVENAGENTS_AGENT_SKILLS_DIR: agentSkillsDir,
1133
1136
  ELEVENAGENTS_AGENT_MEMORY_DIR: agentMemoryDir,
@@ -1146,7 +1149,7 @@ async function prepareRuntimeTask(task, flags, deps, config) {
1146
1149
  agent_results_dir: agentResultsDir,
1147
1150
  tmp_dir: tmpDir,
1148
1151
  run_dir: runDir,
1149
- project_slug: projectSlugForTask(task, flags),
1152
+ project_slug: projectSlug,
1150
1153
  readonly: true,
1151
1154
  env,
1152
1155
  database,
package/src/schema.js CHANGED
@@ -26,7 +26,9 @@ export function validateTelemetryBatch(input) {
26
26
  }
27
27
  const artifacts = Array.isArray(input.artifacts) ? input.artifacts : []
28
28
  const observations = Array.isArray(input.observations) ? input.observations : []
29
- if (!artifacts.length && !observations.length) return fail('artifacts or observations are required')
29
+ if (!artifacts.length && !observations.length && !isObject(input.dashboard_spec)) {
30
+ return fail('artifacts, observations, or dashboard_spec are required')
31
+ }
30
32
  for (let i = 0; i < artifacts.length; i += 1) {
31
33
  const item = artifacts[i]
32
34
  for (const field of ['platform', 'artifact_type', 'external_id', 'created_at']) {
@@ -48,7 +50,7 @@ export function validateTelemetryBatch(input) {
48
50
  return { ok: true, batch: { ...input, agent_id: agentId.trim() } }
49
51
  }
50
52
 
51
- export function buildTelemetryBatch({ workspace, agent_id, agent_key, node_id, artifacts = [], observations = [] }) {
53
+ export function buildTelemetryBatch({ workspace, agent_id, agent_key, node_id, artifacts = [], observations = [], dashboard_spec = null }) {
52
54
  return {
53
55
  schema_version: TELEMETRY_SCHEMA_VERSION,
54
56
  workspace,
@@ -58,5 +60,6 @@ export function buildTelemetryBatch({ workspace, agent_id, agent_key, node_id, a
58
60
  sent_at: new Date().toISOString(),
59
61
  artifacts,
60
62
  observations,
63
+ ...(dashboard_spec ? { dashboard_spec } : {}),
61
64
  }
62
65
  }