@xdxer/dingtalk-agent 0.1.4-beta.11 → 0.1.4-beta.12

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.
@@ -5,17 +5,19 @@
5
5
  // > .dingtalk-agent/agent-platform.json > 按 Provider 声明推断。
6
6
  // 平台相关的方法学不写进 CLI——它们随对应平台的 Skill 包分发,只在用户选择该平台时安装。
7
7
  // 平台命令(如 deploy/promote/observe)必须先通过归属门禁;coming-soon 平台一律 fail-closed。
8
- import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
8
+ import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
9
+ import { homedir } from 'node:os';
9
10
  import { dirname, join, parse, resolve } from 'node:path';
10
11
  import { findAgentProjectRoot } from './development-workspace.js';
12
+ import { findExecutable } from './doctor.js';
11
13
  import { installGlobalSkill } from './skill-manager.js';
12
14
  export const MANAGED_AGENT_PLATFORMS = [
13
15
  {
14
16
  name: 'multica-dingtalk',
15
17
  label: 'Multica (DingTalk)',
16
18
  status: 'supported',
17
- description: '钉钉 Multica 托管平台:受控部署、晋级与观测回流。',
18
- skills: ['dingtalk-agent-deploy', 'dingtalk-agent-boot-multica'],
19
+ description: '钉钉 Multica 托管平台:供给、部署、钉钉机器人绑定、观测与调度。',
20
+ skills: ['dingtalk-agent-deploy', 'dingtalk-agent-boot-multica', 'multica-fde-external'],
19
21
  commands: ['deploy', 'promote', 'observe'],
20
22
  },
21
23
  {
@@ -125,6 +127,48 @@ export function requireSupportedAgentPlatform(command, start = process.cwd(), en
125
127
  export function statusLabel(status) {
126
128
  return status === 'supported' ? '已支持' : '敬请期待';
127
129
  }
130
+ /**
131
+ * 平台工具链就绪检查(只读,不阻断):选择/查看平台归属时提示用户缺什么、怎么补。
132
+ * 强制门禁仍由 requireSupportedAgentPlatform 与各平台命令自身承担。
133
+ */
134
+ export function agentPlatformReadiness(name, env = process.env, home = homedir()) {
135
+ if (name !== 'multica-dingtalk')
136
+ return [];
137
+ const checks = [];
138
+ const cli = findExecutable('multica', env);
139
+ checks.push(cli
140
+ ? { id: 'multica-cli', ok: true, summary: `multica CLI 已安装(${cli})` }
141
+ : {
142
+ id: 'multica-cli', ok: false, summary: '未找到 multica CLI',
143
+ hint: '安装:curl -fsSL https://raw.githubusercontent.com/multica-ai/multica/main/scripts/install.sh | bash',
144
+ });
145
+ checks.push(hasMulticaToken(home)
146
+ ? { id: 'multica-login', ok: true, summary: 'Multica 已登录(本地存在 token)' }
147
+ : {
148
+ id: 'multica-login', ok: false, summary: 'Multica 尚未登录',
149
+ hint: '登录:multica login --server-url <endpoint>(预发环境为 https://pre-fde-workbench.dingtalk.com)',
150
+ });
151
+ if (['HTTPS_PROXY', 'https_proxy', 'ALL_PROXY', 'all_proxy'].some((key) => env[key])) {
152
+ checks.push({
153
+ id: 'proxy-env', ok: false, summary: '检测到代理环境变量,可能阻断 Multica 直连',
154
+ hint: '连接失败时用 env -u HTTPS_PROXY -u https_proxy -u ALL_PROXY -u all_proxy 运行 multica 与平台脚本',
155
+ });
156
+ }
157
+ return checks;
158
+ }
159
+ function hasMulticaToken(home) {
160
+ const candidates = [join(home, '.multica', 'config.json')];
161
+ try {
162
+ for (const entry of readdirSync(join(home, '.multica', 'profiles'))) {
163
+ candidates.push(join(home, '.multica', 'profiles', entry, 'config.json'));
164
+ }
165
+ }
166
+ catch { /* profiles 目录不存在 */ }
167
+ return candidates.some((path) => {
168
+ const config = readJsonOrNull(path);
169
+ return typeof config?.token === 'string' && config.token.trim().length > 0;
170
+ });
171
+ }
128
172
  function resolution(name, source, file) {
129
173
  return {
130
174
  $schema: 'dingtalk-agent/agent-platform-resolution@1',
@@ -1 +1 @@
1
- {"version":3,"file":"agent-platform.js","sourceRoot":"","sources":["../../src/agent-platform.ts"],"names":[],"mappings":"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","sourcesContent":["// Managed Agent Platform 注册表与工作区归属解析。\n//\n// 一个 Agent 工作区归属于至多一个托管平台;归属解析顺序固定:\n// DTA_AGENT_PLATFORM 环境变量 > dingtalk-agent.json#agentPlatform\n// > .dingtalk-agent/agent-platform.json > 按 Provider 声明推断。\n// 平台相关的方法学不写进 CLI——它们随对应平台的 Skill 包分发,只在用户选择该平台时安装。\n// 平台命令(如 deploy/promote/observe)必须先通过归属门禁;coming-soon 平台一律 fail-closed。\n\nimport { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join, parse, resolve } from 'node:path'\n\nimport { findAgentProjectRoot } from './development-workspace.js'\nimport { installGlobalSkill, type GlobalSkillStatus } from './skill-manager.js'\n\nexport type AgentPlatformStatus = 'supported' | 'coming-soon'\n\nexport interface AgentPlatformDefinition {\n name: string\n label: string\n status: AgentPlatformStatus\n description: string\n skills: string[]\n commands: string[]\n}\n\nexport const MANAGED_AGENT_PLATFORMS: AgentPlatformDefinition[] = [\n {\n name: 'multica-dingtalk',\n label: 'Multica (DingTalk)',\n status: 'supported',\n description: '钉钉 Multica 托管平台:受控部署、晋级与观测回流。',\n skills: ['dingtalk-agent-deploy', 'dingtalk-agent-boot-multica'],\n commands: ['deploy', 'promote', 'observe'],\n },\n {\n name: 'deap',\n label: 'DEAP',\n status: 'coming-soon',\n description: '敬请期待。',\n skills: [],\n commands: [],\n },\n]\n\nexport const AGENT_PLATFORM_FILE = '.dingtalk-agent/agent-platform.json'\nconst PROJECT_FILE = 'dingtalk-agent.json'\n\nexport type AgentPlatformSource = 'env' | 'project-manifest' | 'platform-file' | 'inferred' | 'none'\n\nexport interface AgentPlatformResolution {\n $schema: 'dingtalk-agent/agent-platform-resolution@1'\n platform: string | null\n source: AgentPlatformSource\n file: string\n definition: AgentPlatformDefinition | null\n platforms: AgentPlatformDefinition[]\n}\n\nexport interface AgentPlatformUseResult extends AgentPlatformResolution {\n written: string\n skillInstalls: GlobalSkillStatus[]\n}\n\nexport function agentPlatformDefinition(name: string): AgentPlatformDefinition | null {\n return MANAGED_AGENT_PLATFORMS.find((item) => item.name === name) || null\n}\n\nexport function resolveAgentPlatform(\n start = process.cwd(), env: NodeJS.ProcessEnv = process.env,\n): AgentPlatformResolution {\n const fromEnv = String(env.DTA_AGENT_PLATFORM || '').trim()\n if (fromEnv) return resolution(fromEnv, 'env', '')\n\n const projectRoot = findAgentProjectRoot(start)\n if (projectRoot) {\n const manifestPath = join(projectRoot, PROJECT_FILE)\n if (existsSync(manifestPath) && !lstatSync(manifestPath).isSymbolicLink()) {\n const manifest = readJsonOrNull(manifestPath)\n const declared = typeof manifest?.agentPlatform === 'string' ? manifest.agentPlatform.trim() : ''\n if (declared) return resolution(declared, 'project-manifest', manifestPath)\n }\n }\n\n const platformPath = findUp(start, AGENT_PLATFORM_FILE)\n if (platformPath) {\n const stored = readJsonOrNull(platformPath)\n const declared = typeof stored?.agentPlatform === 'string' ? stored.agentPlatform.trim() : ''\n if (declared) return resolution(declared, 'platform-file', platformPath)\n }\n\n if (projectRoot) {\n const manifest = readJsonOrNull(join(projectRoot, PROJECT_FILE))\n const providers = Object.values(manifest?.workspaces || {})\n .map((workspace: any) => workspace?.provider?.kind)\n if (providers.includes('multica')) {\n return resolution('multica-dingtalk', 'inferred', join(projectRoot, PROJECT_FILE))\n }\n }\n\n return resolution(null, 'none', '')\n}\n\nexport interface AgentPlatformUseOptions {\n packageRoot: string\n home?: string\n env?: NodeJS.ProcessEnv\n installSkills?: boolean\n}\n\nexport function useAgentPlatform(\n start: string, name: string, options: AgentPlatformUseOptions,\n): AgentPlatformUseResult {\n const definition = agentPlatformDefinition(name)\n if (!definition) {\n throw new Error(\n `未知 managed agent platform: ${name}\\n` +\n `可用: ${MANAGED_AGENT_PLATFORMS.map((item) => `${item.name}(${statusLabel(item.status)})`).join('、')}`,\n )\n }\n if (definition.status !== 'supported') {\n throw new Error(`${definition.label} 敬请期待——当前版本尚未开放该平台。`)\n }\n\n const projectRoot = findAgentProjectRoot(start)\n let written: string\n if (projectRoot && existsSync(join(projectRoot, PROJECT_FILE))) {\n const manifestPath = join(projectRoot, PROJECT_FILE)\n if (lstatSync(manifestPath).isSymbolicLink()) {\n throw new Error(`${PROJECT_FILE} 不能是符号链接`)\n }\n const manifest = readJsonOrNull(manifestPath)\n if (!manifest || typeof manifest !== 'object') {\n throw new Error(`${PROJECT_FILE} 不是合法 JSON,无法写入 agentPlatform 归属`)\n }\n manifest.agentPlatform = definition.name\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\\n')\n written = manifestPath\n } else {\n const target = join(resolve(start), AGENT_PLATFORM_FILE)\n mkdirSync(dirname(target), { recursive: true })\n writeFileSync(target, JSON.stringify(\n { $schema: 'dingtalk-agent/agent-platform@1', agentPlatform: definition.name }, null, 2,\n ) + '\\n')\n written = target\n }\n\n const skillInstalls: GlobalSkillStatus[] = []\n if (options.installSkills !== false) {\n for (const skill of definition.skills) {\n skillInstalls.push(installGlobalSkill(options.packageRoot, {\n name: skill, home: options.home, env: options.env,\n }))\n }\n }\n\n return { ...resolveAgentPlatform(start, options.env || process.env), written, skillInstalls }\n}\n\n/** deploy/promote/observe 等平台命令的归属门禁:未归属、未知或 coming-soon 一律 fail-closed。 */\nexport function requireSupportedAgentPlatform(\n command: string, start = process.cwd(), env: NodeJS.ProcessEnv = process.env,\n): AgentPlatformResolution {\n const resolved = resolveAgentPlatform(start, env)\n if (!resolved.platform) {\n throw new Error(\n `当前工作区未声明 managed agent platform,无法执行 \\`${command}\\`。\\n` +\n '先运行 `dta agent-platform use multica-dingtalk`,或设置 DTA_AGENT_PLATFORM 环境变量。',\n )\n }\n if (!resolved.definition) {\n throw new Error(\n `工作区声明了未知 platform: ${resolved.platform}(来源 ${resolved.source})。\\n` +\n `可用: ${MANAGED_AGENT_PLATFORMS.map((item) => item.name).join('、')}`,\n )\n }\n if (resolved.definition.status !== 'supported') {\n throw new Error(`${resolved.definition.label} 敬请期待——\\`${command}\\` 尚未在该平台开放。`)\n }\n if (!resolved.definition.commands.includes(command)) {\n throw new Error(`${resolved.definition.label} 不承接命令 \\`${command}\\`。`)\n }\n return resolved\n}\n\nexport function statusLabel(status: AgentPlatformStatus): string {\n return status === 'supported' ? '已支持' : '敬请期待'\n}\n\nfunction resolution(\n name: string | null, source: AgentPlatformSource, file: string,\n): AgentPlatformResolution {\n return {\n $schema: 'dingtalk-agent/agent-platform-resolution@1',\n platform: name,\n source,\n file,\n definition: name ? agentPlatformDefinition(name) : null,\n platforms: MANAGED_AGENT_PLATFORMS,\n }\n}\n\nfunction readJsonOrNull(path: string): any {\n try { return JSON.parse(readFileSync(path, 'utf8')) } catch { return null }\n}\n\nfunction findUp(start: string, relativePath: string): string | null {\n let dir = resolve(start)\n while (true) {\n const candidate = join(dir, relativePath)\n if (existsSync(candidate) && !lstatSync(candidate).isSymbolicLink()) return candidate\n const parent = dirname(dir)\n if (parent === dir || dir === parse(dir).root) return null\n dir = parent\n }\n}\n"]}
1
+ {"version":3,"file":"agent-platform.js","sourceRoot":"","sources":["../../src/agent-platform.ts"],"names":[],"mappings":"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","sourcesContent":["// Managed Agent Platform 注册表与工作区归属解析。\n//\n// 一个 Agent 工作区归属于至多一个托管平台;归属解析顺序固定:\n// DTA_AGENT_PLATFORM 环境变量 > dingtalk-agent.json#agentPlatform\n// > .dingtalk-agent/agent-platform.json > 按 Provider 声明推断。\n// 平台相关的方法学不写进 CLI——它们随对应平台的 Skill 包分发,只在用户选择该平台时安装。\n// 平台命令(如 deploy/promote/observe)必须先通过归属门禁;coming-soon 平台一律 fail-closed。\n\nimport { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, join, parse, resolve } from 'node:path'\n\nimport { findAgentProjectRoot } from './development-workspace.js'\nimport { findExecutable } from './doctor.js'\nimport { installGlobalSkill, type GlobalSkillStatus } from './skill-manager.js'\n\nexport type AgentPlatformStatus = 'supported' | 'coming-soon'\n\nexport interface AgentPlatformDefinition {\n name: string\n label: string\n status: AgentPlatformStatus\n description: string\n skills: string[]\n commands: string[]\n}\n\nexport const MANAGED_AGENT_PLATFORMS: AgentPlatformDefinition[] = [\n {\n name: 'multica-dingtalk',\n label: 'Multica (DingTalk)',\n status: 'supported',\n description: '钉钉 Multica 托管平台:供给、部署、钉钉机器人绑定、观测与调度。',\n skills: ['dingtalk-agent-deploy', 'dingtalk-agent-boot-multica', 'multica-fde-external'],\n commands: ['deploy', 'promote', 'observe'],\n },\n {\n name: 'deap',\n label: 'DEAP',\n status: 'coming-soon',\n description: '敬请期待。',\n skills: [],\n commands: [],\n },\n]\n\nexport const AGENT_PLATFORM_FILE = '.dingtalk-agent/agent-platform.json'\nconst PROJECT_FILE = 'dingtalk-agent.json'\n\nexport type AgentPlatformSource = 'env' | 'project-manifest' | 'platform-file' | 'inferred' | 'none'\n\nexport interface AgentPlatformResolution {\n $schema: 'dingtalk-agent/agent-platform-resolution@1'\n platform: string | null\n source: AgentPlatformSource\n file: string\n definition: AgentPlatformDefinition | null\n platforms: AgentPlatformDefinition[]\n}\n\nexport interface AgentPlatformUseResult extends AgentPlatformResolution {\n written: string\n skillInstalls: GlobalSkillStatus[]\n}\n\nexport function agentPlatformDefinition(name: string): AgentPlatformDefinition | null {\n return MANAGED_AGENT_PLATFORMS.find((item) => item.name === name) || null\n}\n\nexport function resolveAgentPlatform(\n start = process.cwd(), env: NodeJS.ProcessEnv = process.env,\n): AgentPlatformResolution {\n const fromEnv = String(env.DTA_AGENT_PLATFORM || '').trim()\n if (fromEnv) return resolution(fromEnv, 'env', '')\n\n const projectRoot = findAgentProjectRoot(start)\n if (projectRoot) {\n const manifestPath = join(projectRoot, PROJECT_FILE)\n if (existsSync(manifestPath) && !lstatSync(manifestPath).isSymbolicLink()) {\n const manifest = readJsonOrNull(manifestPath)\n const declared = typeof manifest?.agentPlatform === 'string' ? manifest.agentPlatform.trim() : ''\n if (declared) return resolution(declared, 'project-manifest', manifestPath)\n }\n }\n\n const platformPath = findUp(start, AGENT_PLATFORM_FILE)\n if (platformPath) {\n const stored = readJsonOrNull(platformPath)\n const declared = typeof stored?.agentPlatform === 'string' ? stored.agentPlatform.trim() : ''\n if (declared) return resolution(declared, 'platform-file', platformPath)\n }\n\n if (projectRoot) {\n const manifest = readJsonOrNull(join(projectRoot, PROJECT_FILE))\n const providers = Object.values(manifest?.workspaces || {})\n .map((workspace: any) => workspace?.provider?.kind)\n if (providers.includes('multica')) {\n return resolution('multica-dingtalk', 'inferred', join(projectRoot, PROJECT_FILE))\n }\n }\n\n return resolution(null, 'none', '')\n}\n\nexport interface AgentPlatformUseOptions {\n packageRoot: string\n home?: string\n env?: NodeJS.ProcessEnv\n installSkills?: boolean\n}\n\nexport function useAgentPlatform(\n start: string, name: string, options: AgentPlatformUseOptions,\n): AgentPlatformUseResult {\n const definition = agentPlatformDefinition(name)\n if (!definition) {\n throw new Error(\n `未知 managed agent platform: ${name}\\n` +\n `可用: ${MANAGED_AGENT_PLATFORMS.map((item) => `${item.name}(${statusLabel(item.status)})`).join('、')}`,\n )\n }\n if (definition.status !== 'supported') {\n throw new Error(`${definition.label} 敬请期待——当前版本尚未开放该平台。`)\n }\n\n const projectRoot = findAgentProjectRoot(start)\n let written: string\n if (projectRoot && existsSync(join(projectRoot, PROJECT_FILE))) {\n const manifestPath = join(projectRoot, PROJECT_FILE)\n if (lstatSync(manifestPath).isSymbolicLink()) {\n throw new Error(`${PROJECT_FILE} 不能是符号链接`)\n }\n const manifest = readJsonOrNull(manifestPath)\n if (!manifest || typeof manifest !== 'object') {\n throw new Error(`${PROJECT_FILE} 不是合法 JSON,无法写入 agentPlatform 归属`)\n }\n manifest.agentPlatform = definition.name\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\\n')\n written = manifestPath\n } else {\n const target = join(resolve(start), AGENT_PLATFORM_FILE)\n mkdirSync(dirname(target), { recursive: true })\n writeFileSync(target, JSON.stringify(\n { $schema: 'dingtalk-agent/agent-platform@1', agentPlatform: definition.name }, null, 2,\n ) + '\\n')\n written = target\n }\n\n const skillInstalls: GlobalSkillStatus[] = []\n if (options.installSkills !== false) {\n for (const skill of definition.skills) {\n skillInstalls.push(installGlobalSkill(options.packageRoot, {\n name: skill, home: options.home, env: options.env,\n }))\n }\n }\n\n return { ...resolveAgentPlatform(start, options.env || process.env), written, skillInstalls }\n}\n\n/** deploy/promote/observe 等平台命令的归属门禁:未归属、未知或 coming-soon 一律 fail-closed。 */\nexport function requireSupportedAgentPlatform(\n command: string, start = process.cwd(), env: NodeJS.ProcessEnv = process.env,\n): AgentPlatformResolution {\n const resolved = resolveAgentPlatform(start, env)\n if (!resolved.platform) {\n throw new Error(\n `当前工作区未声明 managed agent platform,无法执行 \\`${command}\\`。\\n` +\n '先运行 `dta agent-platform use multica-dingtalk`,或设置 DTA_AGENT_PLATFORM 环境变量。',\n )\n }\n if (!resolved.definition) {\n throw new Error(\n `工作区声明了未知 platform: ${resolved.platform}(来源 ${resolved.source})。\\n` +\n `可用: ${MANAGED_AGENT_PLATFORMS.map((item) => item.name).join('、')}`,\n )\n }\n if (resolved.definition.status !== 'supported') {\n throw new Error(`${resolved.definition.label} 敬请期待——\\`${command}\\` 尚未在该平台开放。`)\n }\n if (!resolved.definition.commands.includes(command)) {\n throw new Error(`${resolved.definition.label} 不承接命令 \\`${command}\\`。`)\n }\n return resolved\n}\n\nexport function statusLabel(status: AgentPlatformStatus): string {\n return status === 'supported' ? '已支持' : '敬请期待'\n}\n\nexport interface AgentPlatformReadinessCheck {\n id: string\n ok: boolean\n summary: string\n hint?: string\n}\n\n/**\n * 平台工具链就绪检查(只读,不阻断):选择/查看平台归属时提示用户缺什么、怎么补。\n * 强制门禁仍由 requireSupportedAgentPlatform 与各平台命令自身承担。\n */\nexport function agentPlatformReadiness(\n name: string, env: NodeJS.ProcessEnv = process.env, home = homedir(),\n): AgentPlatformReadinessCheck[] {\n if (name !== 'multica-dingtalk') return []\n const checks: AgentPlatformReadinessCheck[] = []\n const cli = findExecutable('multica', env)\n checks.push(cli\n ? { id: 'multica-cli', ok: true, summary: `multica CLI 已安装(${cli})` }\n : {\n id: 'multica-cli', ok: false, summary: '未找到 multica CLI',\n hint: '安装:curl -fsSL https://raw.githubusercontent.com/multica-ai/multica/main/scripts/install.sh | bash',\n })\n checks.push(hasMulticaToken(home)\n ? { id: 'multica-login', ok: true, summary: 'Multica 已登录(本地存在 token)' }\n : {\n id: 'multica-login', ok: false, summary: 'Multica 尚未登录',\n hint: '登录:multica login --server-url <endpoint>(预发环境为 https://pre-fde-workbench.dingtalk.com)',\n })\n if (['HTTPS_PROXY', 'https_proxy', 'ALL_PROXY', 'all_proxy'].some((key) => env[key])) {\n checks.push({\n id: 'proxy-env', ok: false, summary: '检测到代理环境变量,可能阻断 Multica 直连',\n hint: '连接失败时用 env -u HTTPS_PROXY -u https_proxy -u ALL_PROXY -u all_proxy 运行 multica 与平台脚本',\n })\n }\n return checks\n}\n\nfunction hasMulticaToken(home: string): boolean {\n const candidates = [join(home, '.multica', 'config.json')]\n try {\n for (const entry of readdirSync(join(home, '.multica', 'profiles'))) {\n candidates.push(join(home, '.multica', 'profiles', entry, 'config.json'))\n }\n } catch { /* profiles 目录不存在 */ }\n return candidates.some((path) => {\n const config = readJsonOrNull(path)\n return typeof config?.token === 'string' && config.token.trim().length > 0\n })\n}\n\nfunction resolution(\n name: string | null, source: AgentPlatformSource, file: string,\n): AgentPlatformResolution {\n return {\n $schema: 'dingtalk-agent/agent-platform-resolution@1',\n platform: name,\n source,\n file,\n definition: name ? agentPlatformDefinition(name) : null,\n platforms: MANAGED_AGENT_PLATFORMS,\n }\n}\n\nfunction readJsonOrNull(path: string): any {\n try { return JSON.parse(readFileSync(path, 'utf8')) } catch { return null }\n}\n\nfunction findUp(start: string, relativePath: string): string | null {\n let dir = resolve(start)\n while (true) {\n const candidate = join(dir, relativePath)\n if (existsSync(candidate) && !lstatSync(candidate).isSymbolicLink()) return candidate\n const parent = dirname(dir)\n if (parent === dir || dir === parse(dir).root) return null\n dir = parent\n }\n}\n"]}
@@ -10,7 +10,8 @@ export const GLOBAL_SKILL_NAME = 'dingtalk-basic-behavior';
10
10
  // 只有工作区通过 `dta map use` 选择对应 managed agents platform 时才按名安装。
11
11
  export const BUNDLED_SKILL_NAMES = [GLOBAL_SKILL_NAME, 'dingtalk-agent-compose'];
12
12
  export const INSTALLABLE_SKILL_NAMES = [
13
- ...BUNDLED_SKILL_NAMES, 'dingtalk-agent-deploy', 'dingtalk-agent-boot-multica',
13
+ ...BUNDLED_SKILL_NAMES,
14
+ 'dingtalk-agent-deploy', 'dingtalk-agent-boot-multica', 'multica-fde-external',
14
15
  ];
15
16
  const SKILLS_PACKAGE = 'skills@latest';
16
17
  const REGISTRY = 'https://registry.npmjs.org';
@@ -1 +1 @@
1
- {"version":3,"file":"skill-manager.js","sourceRoot":"","sources":["../../src/skill-manager.ts"],"names":[],"mappings":"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","sourcesContent":["// Skill 安装生命周期由开放生态的 `npx skills` 负责。\n// dingtalk-agent 只做两件事:把内置 Skill 目录交给上游 CLI;只读验证客户端能否发现。\n// 默认(不指定 name)对全部内置 Skill 生效:基础行为 + Agent 装配;指定 name 时只操作该 Skill。\n\nimport { existsSync, lstatSync, readFileSync, readlinkSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, join, resolve } from 'node:path'\nimport { spawnSync } from 'node:child_process'\n\nexport const GLOBAL_SKILL_NAME = 'dingtalk-basic-behavior'\n// 默认套装:所有机器都装的通用 Skill。平台 Skill(deploy/boot-multica 等)不在默认套装内,\n// 只有工作区通过 `dta map use` 选择对应 managed agents platform 时才按名安装。\nexport const BUNDLED_SKILL_NAMES = [GLOBAL_SKILL_NAME, 'dingtalk-agent-compose'] as const\nexport const INSTALLABLE_SKILL_NAMES = [\n ...BUNDLED_SKILL_NAMES, 'dingtalk-agent-deploy', 'dingtalk-agent-boot-multica',\n] as const\nconst SKILLS_PACKAGE = 'skills@latest'\nconst REGISTRY = 'https://registry.npmjs.org'\nconst AGENTS = ['claude-code', 'codex', 'opencode']\n\nexport interface SkillManagerOptions {\n home?: string\n dryRun?: boolean\n env?: NodeJS.ProcessEnv\n name?: string\n}\n\nexport interface GlobalSkillStatus {\n $schema: 'dingtalk-agent/skill-status@2'\n name: string\n manager: 'skills-cli'\n bundled: { path: string; packageVersion: string; skillVersion: string }\n canonical: {\n path: string\n exists: boolean\n installedVersion: string\n current: boolean\n }\n claude: { path: string; state: 'ok' | 'missing' | 'conflict'; target: string }\n clients: Array<{\n name: 'claude-code' | 'codex' | 'opencode'\n label: string\n discovery: 'symlink' | 'shared-agent-skills'\n path: string\n state: 'ok' | 'missing' | 'conflict'\n }>\n operation?: {\n action: 'install' | 'upgrade' | 'uninstall'\n command: string\n executed: boolean\n exitCode: number | null\n output: string\n }\n}\n\nexport interface GlobalSkillSuiteStatus {\n $schema: 'dingtalk-agent/skill-status-suite@1'\n manager: 'skills-cli'\n names: string[]\n skills: GlobalSkillStatus[]\n}\n\nexport function skillSuiteStatus(\n packageRoot: string,\n options: Omit<SkillManagerOptions, 'name'> = {},\n): GlobalSkillSuiteStatus {\n return suiteOf(BUNDLED_SKILL_NAMES.map((name) => skillStatus(packageRoot, { ...options, name })))\n}\n\nexport function installBundledSkills(\n packageRoot: string,\n options: Omit<SkillManagerOptions, 'name'> = {},\n): GlobalSkillSuiteStatus {\n return suiteOf(BUNDLED_SKILL_NAMES.map((name) => installGlobalSkill(packageRoot, { ...options, name })))\n}\n\nexport function upgradeBundledSkills(\n packageRoot: string,\n options: Omit<SkillManagerOptions, 'name'> = {},\n): GlobalSkillSuiteStatus {\n return suiteOf(BUNDLED_SKILL_NAMES.map((name) => upgradeGlobalSkill(packageRoot, { ...options, name })))\n}\n\nexport function uninstallBundledSkills(\n packageRoot: string,\n options: Omit<SkillManagerOptions, 'name'> = {},\n): GlobalSkillSuiteStatus {\n return suiteOf(BUNDLED_SKILL_NAMES.map((name) => uninstallGlobalSkill(packageRoot, { ...options, name })))\n}\n\nfunction suiteOf(skills: GlobalSkillStatus[]): GlobalSkillSuiteStatus {\n return {\n $schema: 'dingtalk-agent/skill-status-suite@1',\n manager: 'skills-cli',\n names: skills.map((skill) => skill.name),\n skills,\n }\n}\n\nexport function skillStatus(packageRoot: string, options: SkillManagerOptions = {}): GlobalSkillStatus {\n const name = bundledSkillName(options.name)\n const paths = globalPaths(packageRoot, options.home, name)\n const bundled = bundledInfo(packageRoot, name)\n const canonicalExists = isDirectory(paths.canonical)\n const installedVersion = canonicalExists\n ? parseSkillFrontmatter(readFileSync(join(paths.canonical, 'SKILL.md'), 'utf8')).version\n : ''\n const claude = inspectExposure(paths.claude, paths.canonical)\n const sharedState = canonicalExists ? 'ok' : 'missing'\n return {\n $schema: 'dingtalk-agent/skill-status@2',\n name,\n manager: 'skills-cli',\n bundled,\n canonical: {\n path: paths.canonical,\n exists: canonicalExists,\n installedVersion,\n current: Boolean(canonicalExists && installedVersion === bundled.skillVersion),\n },\n claude: { path: paths.claude, ...claude },\n clients: [\n {\n name: 'claude-code', label: 'Claude Code', discovery: 'symlink',\n path: paths.claude, state: claude.state,\n },\n {\n name: 'codex', label: 'Codex', discovery: 'shared-agent-skills',\n path: paths.canonical, state: sharedState,\n },\n {\n name: 'opencode', label: 'OpenCode', discovery: 'shared-agent-skills',\n path: paths.canonical, state: sharedState,\n },\n ],\n }\n}\n\nexport function installGlobalSkill(\n packageRoot: string,\n options: SkillManagerOptions = {},\n): GlobalSkillStatus {\n return runSkills(packageRoot, 'install', [\n 'add', globalPaths(packageRoot, undefined, bundledSkillName(options.name)).source,\n '--global', '--yes', '--agent', ...AGENTS,\n ], options)\n}\n\nexport function upgradeGlobalSkill(\n packageRoot: string,\n options: SkillManagerOptions = {},\n): GlobalSkillStatus {\n // Skill 随 npm 包发布;重新 add 当前包内目录,让 skills CLI 执行自己的覆盖/链接语义。\n return runSkills(packageRoot, 'upgrade', [\n 'add', globalPaths(packageRoot, undefined, bundledSkillName(options.name)).source,\n '--global', '--yes', '--agent', ...AGENTS,\n ], options)\n}\n\nexport function uninstallGlobalSkill(\n packageRoot: string,\n options: SkillManagerOptions = {},\n): GlobalSkillStatus {\n return runSkills(packageRoot, 'uninstall', [\n 'remove', bundledSkillName(options.name),\n '--global', '--yes', '--agent', ...AGENTS,\n ], options)\n}\n\nfunction runSkills(\n packageRoot: string,\n action: 'install' | 'upgrade' | 'uninstall',\n args: string[],\n options: SkillManagerOptions,\n): GlobalSkillStatus {\n const commandArgs = [\n '--yes', `--registry=${REGISTRY}`, `--package=${SKILLS_PACKAGE}`, '--', 'skills', ...args,\n ]\n const command = ['npx', ...commandArgs].map(shellQuote).join(' ')\n const before = skillStatus(packageRoot, options)\n if (options.dryRun) {\n return {\n ...before,\n operation: { action, command, executed: false, exitCode: null, output: '' },\n }\n }\n\n const env = {\n ...process.env,\n ...(options.env || {}),\n HOME: options.home || options.env?.HOME || process.env.HOME || homedir(),\n }\n const run = spawnSync('npx', commandArgs, {\n encoding: 'utf8', env, timeout: 120_000,\n })\n const output = [run.stdout, run.stderr].filter(Boolean).join('\\n').trim()\n if (run.error || run.status !== 0) {\n const reason = (run.error as NodeJS.ErrnoException | undefined)?.code === 'ENOENT'\n ? 'PATH 找不到 npx;请安装带 npm/npx 的 Node.js 18.3+'\n : run.error?.message || output || `退出码 ${run.status}`\n throw new Error(\n `skills CLI ${action} 失败: ${reason}\\n` +\n `可手动执行: ${command}`,\n )\n }\n return {\n ...skillStatus(packageRoot, options),\n operation: { action, command, executed: true, exitCode: run.status, output },\n }\n}\n\nfunction globalPaths(packageRoot: string, home = homedir(), name = GLOBAL_SKILL_NAME) {\n return {\n source: join(packageRoot, 'skills', name),\n canonical: join(home, '.agents', 'skills', name),\n claude: join(home, '.claude', 'skills', name),\n }\n}\n\nfunction bundledInfo(packageRoot: string, name: string) {\n const source = globalPaths(packageRoot, undefined, name).source\n validateSkill(source, name)\n const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'))\n const frontmatter = parseSkillFrontmatter(readFileSync(join(source, 'SKILL.md'), 'utf8'))\n return {\n path: source,\n packageVersion: String(pkg.version || ''),\n skillVersion: frontmatter.version,\n }\n}\n\nfunction inspectExposure(exposure: string, canonical: string): {\n state: 'ok' | 'missing' | 'conflict'\n target: string\n} {\n if (!pathExists(exposure)) return { state: 'missing', target: '' }\n const stat = lstatSync(exposure)\n if (!stat.isSymbolicLink()) return { state: 'conflict', target: '' }\n const raw = readlinkSync(exposure)\n return resolve(dirname(exposure), raw) === resolve(canonical)\n ? { state: 'ok', target: raw }\n : { state: 'conflict', target: raw }\n}\n\nfunction validateSkill(root: string, name: string): void {\n if (!isDirectory(root)) throw new Error(`内置 Skill 不存在: ${root}`)\n const file = join(root, 'SKILL.md')\n if (!existsSync(file)) throw new Error(`Skill 缺少 SKILL.md: ${root}`)\n const metadata = parseSkillFrontmatter(readFileSync(file, 'utf8'))\n if (metadata.name !== name) {\n throw new Error(`Skill name=${metadata.name} 与目录名 ${name} 不一致`)\n }\n if (!metadata.description) throw new Error('Skill frontmatter 缺少 description')\n}\n\nfunction bundledSkillName(value = ''): string {\n const name = value.trim() || GLOBAL_SKILL_NAME\n if (!(INSTALLABLE_SKILL_NAMES as readonly string[]).includes(name)) {\n throw new Error(`不支持的内置 Skill: ${name};可用: ${INSTALLABLE_SKILL_NAMES.join(', ')}`)\n }\n return name\n}\n\nfunction parseSkillFrontmatter(text: string): { name: string; description: string; version: string } {\n const match = text.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---(?:\\r?\\n|$)/)\n if (!match) throw new Error('SKILL.md 缺少 YAML frontmatter')\n const block = match[1]\n const readScalar = (key: string) => {\n const found = block.match(new RegExp(`^${key}:\\\\s*(.+)$`, 'm'))\n return found ? found[1].trim().replace(/^(['\"])(.*)\\1$/, '$2') : ''\n }\n const nestedVersion = block.match(/^metadata:\\s*\\r?\\n(?:[ \\t]+.*\\r?\\n)*?[ \\t]+version:\\s*['\"]?([^'\"\\r\\n]+)['\"]?\\s*$/m)\n return {\n name: readScalar('name'),\n description: readScalar('description'),\n version: nestedVersion?.[1]?.trim() || readScalar('version'),\n }\n}\n\nfunction isDirectory(path: string): boolean {\n try { return lstatSync(path).isDirectory() } catch { return false }\n}\n\nfunction pathExists(path: string): boolean {\n try { lstatSync(path); return true } catch { return false }\n}\n\nfunction shellQuote(value: string): string {\n return /^[A-Za-z0-9_./:=@-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\"'\"'`)}'`\n}\n"]}
1
+ {"version":3,"file":"skill-manager.js","sourceRoot":"","sources":["../../src/skill-manager.ts"],"names":[],"mappings":"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","sourcesContent":["// Skill 安装生命周期由开放生态的 `npx skills` 负责。\n// dingtalk-agent 只做两件事:把内置 Skill 目录交给上游 CLI;只读验证客户端能否发现。\n// 默认(不指定 name)对全部内置 Skill 生效:基础行为 + Agent 装配;指定 name 时只操作该 Skill。\n\nimport { existsSync, lstatSync, readFileSync, readlinkSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, join, resolve } from 'node:path'\nimport { spawnSync } from 'node:child_process'\n\nexport const GLOBAL_SKILL_NAME = 'dingtalk-basic-behavior'\n// 默认套装:所有机器都装的通用 Skill。平台 Skill(deploy/boot-multica 等)不在默认套装内,\n// 只有工作区通过 `dta map use` 选择对应 managed agents platform 时才按名安装。\nexport const BUNDLED_SKILL_NAMES = [GLOBAL_SKILL_NAME, 'dingtalk-agent-compose'] as const\nexport const INSTALLABLE_SKILL_NAMES = [\n ...BUNDLED_SKILL_NAMES,\n 'dingtalk-agent-deploy', 'dingtalk-agent-boot-multica', 'multica-fde-external',\n] as const\nconst SKILLS_PACKAGE = 'skills@latest'\nconst REGISTRY = 'https://registry.npmjs.org'\nconst AGENTS = ['claude-code', 'codex', 'opencode']\n\nexport interface SkillManagerOptions {\n home?: string\n dryRun?: boolean\n env?: NodeJS.ProcessEnv\n name?: string\n}\n\nexport interface GlobalSkillStatus {\n $schema: 'dingtalk-agent/skill-status@2'\n name: string\n manager: 'skills-cli'\n bundled: { path: string; packageVersion: string; skillVersion: string }\n canonical: {\n path: string\n exists: boolean\n installedVersion: string\n current: boolean\n }\n claude: { path: string; state: 'ok' | 'missing' | 'conflict'; target: string }\n clients: Array<{\n name: 'claude-code' | 'codex' | 'opencode'\n label: string\n discovery: 'symlink' | 'shared-agent-skills'\n path: string\n state: 'ok' | 'missing' | 'conflict'\n }>\n operation?: {\n action: 'install' | 'upgrade' | 'uninstall'\n command: string\n executed: boolean\n exitCode: number | null\n output: string\n }\n}\n\nexport interface GlobalSkillSuiteStatus {\n $schema: 'dingtalk-agent/skill-status-suite@1'\n manager: 'skills-cli'\n names: string[]\n skills: GlobalSkillStatus[]\n}\n\nexport function skillSuiteStatus(\n packageRoot: string,\n options: Omit<SkillManagerOptions, 'name'> = {},\n): GlobalSkillSuiteStatus {\n return suiteOf(BUNDLED_SKILL_NAMES.map((name) => skillStatus(packageRoot, { ...options, name })))\n}\n\nexport function installBundledSkills(\n packageRoot: string,\n options: Omit<SkillManagerOptions, 'name'> = {},\n): GlobalSkillSuiteStatus {\n return suiteOf(BUNDLED_SKILL_NAMES.map((name) => installGlobalSkill(packageRoot, { ...options, name })))\n}\n\nexport function upgradeBundledSkills(\n packageRoot: string,\n options: Omit<SkillManagerOptions, 'name'> = {},\n): GlobalSkillSuiteStatus {\n return suiteOf(BUNDLED_SKILL_NAMES.map((name) => upgradeGlobalSkill(packageRoot, { ...options, name })))\n}\n\nexport function uninstallBundledSkills(\n packageRoot: string,\n options: Omit<SkillManagerOptions, 'name'> = {},\n): GlobalSkillSuiteStatus {\n return suiteOf(BUNDLED_SKILL_NAMES.map((name) => uninstallGlobalSkill(packageRoot, { ...options, name })))\n}\n\nfunction suiteOf(skills: GlobalSkillStatus[]): GlobalSkillSuiteStatus {\n return {\n $schema: 'dingtalk-agent/skill-status-suite@1',\n manager: 'skills-cli',\n names: skills.map((skill) => skill.name),\n skills,\n }\n}\n\nexport function skillStatus(packageRoot: string, options: SkillManagerOptions = {}): GlobalSkillStatus {\n const name = bundledSkillName(options.name)\n const paths = globalPaths(packageRoot, options.home, name)\n const bundled = bundledInfo(packageRoot, name)\n const canonicalExists = isDirectory(paths.canonical)\n const installedVersion = canonicalExists\n ? parseSkillFrontmatter(readFileSync(join(paths.canonical, 'SKILL.md'), 'utf8')).version\n : ''\n const claude = inspectExposure(paths.claude, paths.canonical)\n const sharedState = canonicalExists ? 'ok' : 'missing'\n return {\n $schema: 'dingtalk-agent/skill-status@2',\n name,\n manager: 'skills-cli',\n bundled,\n canonical: {\n path: paths.canonical,\n exists: canonicalExists,\n installedVersion,\n current: Boolean(canonicalExists && installedVersion === bundled.skillVersion),\n },\n claude: { path: paths.claude, ...claude },\n clients: [\n {\n name: 'claude-code', label: 'Claude Code', discovery: 'symlink',\n path: paths.claude, state: claude.state,\n },\n {\n name: 'codex', label: 'Codex', discovery: 'shared-agent-skills',\n path: paths.canonical, state: sharedState,\n },\n {\n name: 'opencode', label: 'OpenCode', discovery: 'shared-agent-skills',\n path: paths.canonical, state: sharedState,\n },\n ],\n }\n}\n\nexport function installGlobalSkill(\n packageRoot: string,\n options: SkillManagerOptions = {},\n): GlobalSkillStatus {\n return runSkills(packageRoot, 'install', [\n 'add', globalPaths(packageRoot, undefined, bundledSkillName(options.name)).source,\n '--global', '--yes', '--agent', ...AGENTS,\n ], options)\n}\n\nexport function upgradeGlobalSkill(\n packageRoot: string,\n options: SkillManagerOptions = {},\n): GlobalSkillStatus {\n // Skill 随 npm 包发布;重新 add 当前包内目录,让 skills CLI 执行自己的覆盖/链接语义。\n return runSkills(packageRoot, 'upgrade', [\n 'add', globalPaths(packageRoot, undefined, bundledSkillName(options.name)).source,\n '--global', '--yes', '--agent', ...AGENTS,\n ], options)\n}\n\nexport function uninstallGlobalSkill(\n packageRoot: string,\n options: SkillManagerOptions = {},\n): GlobalSkillStatus {\n return runSkills(packageRoot, 'uninstall', [\n 'remove', bundledSkillName(options.name),\n '--global', '--yes', '--agent', ...AGENTS,\n ], options)\n}\n\nfunction runSkills(\n packageRoot: string,\n action: 'install' | 'upgrade' | 'uninstall',\n args: string[],\n options: SkillManagerOptions,\n): GlobalSkillStatus {\n const commandArgs = [\n '--yes', `--registry=${REGISTRY}`, `--package=${SKILLS_PACKAGE}`, '--', 'skills', ...args,\n ]\n const command = ['npx', ...commandArgs].map(shellQuote).join(' ')\n const before = skillStatus(packageRoot, options)\n if (options.dryRun) {\n return {\n ...before,\n operation: { action, command, executed: false, exitCode: null, output: '' },\n }\n }\n\n const env = {\n ...process.env,\n ...(options.env || {}),\n HOME: options.home || options.env?.HOME || process.env.HOME || homedir(),\n }\n const run = spawnSync('npx', commandArgs, {\n encoding: 'utf8', env, timeout: 120_000,\n })\n const output = [run.stdout, run.stderr].filter(Boolean).join('\\n').trim()\n if (run.error || run.status !== 0) {\n const reason = (run.error as NodeJS.ErrnoException | undefined)?.code === 'ENOENT'\n ? 'PATH 找不到 npx;请安装带 npm/npx 的 Node.js 18.3+'\n : run.error?.message || output || `退出码 ${run.status}`\n throw new Error(\n `skills CLI ${action} 失败: ${reason}\\n` +\n `可手动执行: ${command}`,\n )\n }\n return {\n ...skillStatus(packageRoot, options),\n operation: { action, command, executed: true, exitCode: run.status, output },\n }\n}\n\nfunction globalPaths(packageRoot: string, home = homedir(), name = GLOBAL_SKILL_NAME) {\n return {\n source: join(packageRoot, 'skills', name),\n canonical: join(home, '.agents', 'skills', name),\n claude: join(home, '.claude', 'skills', name),\n }\n}\n\nfunction bundledInfo(packageRoot: string, name: string) {\n const source = globalPaths(packageRoot, undefined, name).source\n validateSkill(source, name)\n const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'))\n const frontmatter = parseSkillFrontmatter(readFileSync(join(source, 'SKILL.md'), 'utf8'))\n return {\n path: source,\n packageVersion: String(pkg.version || ''),\n skillVersion: frontmatter.version,\n }\n}\n\nfunction inspectExposure(exposure: string, canonical: string): {\n state: 'ok' | 'missing' | 'conflict'\n target: string\n} {\n if (!pathExists(exposure)) return { state: 'missing', target: '' }\n const stat = lstatSync(exposure)\n if (!stat.isSymbolicLink()) return { state: 'conflict', target: '' }\n const raw = readlinkSync(exposure)\n return resolve(dirname(exposure), raw) === resolve(canonical)\n ? { state: 'ok', target: raw }\n : { state: 'conflict', target: raw }\n}\n\nfunction validateSkill(root: string, name: string): void {\n if (!isDirectory(root)) throw new Error(`内置 Skill 不存在: ${root}`)\n const file = join(root, 'SKILL.md')\n if (!existsSync(file)) throw new Error(`Skill 缺少 SKILL.md: ${root}`)\n const metadata = parseSkillFrontmatter(readFileSync(file, 'utf8'))\n if (metadata.name !== name) {\n throw new Error(`Skill name=${metadata.name} 与目录名 ${name} 不一致`)\n }\n if (!metadata.description) throw new Error('Skill frontmatter 缺少 description')\n}\n\nfunction bundledSkillName(value = ''): string {\n const name = value.trim() || GLOBAL_SKILL_NAME\n if (!(INSTALLABLE_SKILL_NAMES as readonly string[]).includes(name)) {\n throw new Error(`不支持的内置 Skill: ${name};可用: ${INSTALLABLE_SKILL_NAMES.join(', ')}`)\n }\n return name\n}\n\nfunction parseSkillFrontmatter(text: string): { name: string; description: string; version: string } {\n const match = text.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---(?:\\r?\\n|$)/)\n if (!match) throw new Error('SKILL.md 缺少 YAML frontmatter')\n const block = match[1]\n const readScalar = (key: string) => {\n const found = block.match(new RegExp(`^${key}:\\\\s*(.+)$`, 'm'))\n return found ? found[1].trim().replace(/^(['\"])(.*)\\1$/, '$2') : ''\n }\n const nestedVersion = block.match(/^metadata:\\s*\\r?\\n(?:[ \\t]+.*\\r?\\n)*?[ \\t]+version:\\s*['\"]?([^'\"\\r\\n]+)['\"]?\\s*$/m)\n return {\n name: readScalar('name'),\n description: readScalar('description'),\n version: nestedVersion?.[1]?.trim() || readScalar('version'),\n }\n}\n\nfunction isDirectory(path: string): boolean {\n try { return lstatSync(path).isDirectory() } catch { return false }\n}\n\nfunction pathExists(path: string): boolean {\n try { lstatSync(path); return true } catch { return false }\n}\n\nfunction shellQuote(value: string): string {\n return /^[A-Za-z0-9_./:=@-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\"'\"'`)}'`\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdxer/dingtalk-agent",
3
- "version": "0.1.4-beta.11",
3
+ "version": "0.1.4-beta.12",
4
4
  "description": "钉钉数字员工的 Skill-first 行为范式:全局 Skill 决策,CLI 固定事务边界,Workspace 按需。",
5
5
  "keywords": [
6
6
  "dingtalk",
@@ -22,6 +22,7 @@
22
22
  "skills/dingtalk-agent-compose",
23
23
  "skills/dingtalk-agent-deploy",
24
24
  "skills/dingtalk-agent-eval",
25
+ "skills/multica-fde-external",
25
26
  "examples/agents",
26
27
  "templates/behaviors",
27
28
  "templates/fields",
@@ -3,7 +3,7 @@ name: dingtalk-agent-compose
3
3
  description: 当用户要创建、新建、装配一个 Agent 或钉钉数字员工——包括把 GitHub 仓库、本地文件夹或钉钉文档定义成 Agent,或要审计、补齐、优化 Agent 的 AGENTS.md、本体职责、岗位 Skills、记忆/知识/产物存储与 DWS 权限绑定时使用。即使尚未 init Workspace,也按 dingtalk-agent 的 AgentDefinition 范式给出可运行的最小装配方案;不负责事件触发器。
4
4
  compatibility: Requires dingtalk-agent on PATH; remote DingTalk documents require authenticated dws.
5
5
  metadata:
6
- version: "0.5.1"
6
+ version: "0.6.0"
7
7
  ---
8
8
 
9
9
  # 装配一个可工作的钉钉数字员工 Agent
@@ -13,12 +13,13 @@ metadata:
13
13
  ## 工作顺序
14
14
 
15
15
  1. 识别来源和运行方式:GitHub 先由宿主 clone/checkout,本 Skill 不接管凭证;本地目录直接读取;钉钉文档只承担 memory/knowledge 等远端语义状态。本体 `AGENTS.md` 与 Role Skills 保持在本地、可版本化。
16
- 2. 对已有仓库优先运行 `dta agent enhance --project-name <name> --role-skill <role> --dry-run --json`。它只生成 `agent-enhancement-plan@1`,不会写文件、访问 DWS 或创建 Trigger。审阅 operations、blockers semanticReview 后,才复制计划给出的命令,用同一组参数、当前 `planId` `--yes` 落盘。
17
- 3. apply 只允许本地文件副作用:先把被更新的旧文件备份到 `.dingtalk-agent/backups/agent-enhance/<operationId>/`,再写入并按 hash 回读;自定义 private state 目录必须同步进入 `.gitignore`。输入漂移、planId 过期、非法 Role 路径、未知 OpenCode instruction、路径越界或 symlink 都必须 fail closed。不要跳过 plan,也不要把 `--yes` 写进无人审阅的默认脚本。
18
- 4. 审核本体:优先使用 `AGENTS.md` 表达长期身份、职责、交付物、拒绝边界和协作方式。CLI 只补骨架,绝不虚构这些语义;只要 `AGENTS.md` Role Skill 仍含模板 `<...>`,`agent audit` 必须保持 `partial`。
19
- 5. 审核能力:Basic Behavior 是所有钉钉员工共享且**每个 Session 必须加载**的协议;岗位知识和流程拆到独立 Role/Workflow Skill。目录可发现不等于正文已加载,必须为目标 Agent Host 生成可验证的加载合同。不要把 FDE、招聘、事故处理等岗位方法写回 Basic Skill。
20
- 6. 审核存储:明确工作记忆、业务事实、长期知识、产物和宿主私有控制状态分别去哪。介质可以换,语义层与控制状态不能混。远端 memory/knowledge plan 必须显式绑定 profile expected user,但 enhance 本身仍不读写远端。
21
- 7. 运行 `dta bootstrap --bindings agent.bindings.json --json` `dta agent audit --bindings agent.bindings.json --require-skill <role> --json`。静态配置和真实语义通过后再加 `--verify-load --yes`;不能把“写出了文件”或“目录存在”当成装配完成。
16
+ 2. **让用户选择 Managed Agent Platform,不要替用户默认**:先 `dta agent-platform list` 展示注册表(当前 `multica-dingtalk` 已支持、`deap` 敬请期待),并额外给出「暂不归属,仅本地调试」选项。用户选定托管平台后运行 `dta agent-platform use <platform>`——它写入归属声明并按需安装平台技能包(`multica-dingtalk` 对应 `dingtalk-agent-deploy`、`dingtalk-agent-boot-multica` `multica-fde-external`)。命令会同时输出 readiness 检查:multica CLI 未安装时按提示安装(`curl -fsSL https://raw.githubusercontent.com/multica-ai/multica/main/scripts/install.sh | bash`),未登录时引导 `multica login --server-url <endpoint>`(预发环境为 `https://pre-fde-workbench.dingtalk.com`),检测到代理环境变量时提醒连接失败可用 `env -u` 剥离。readiness 未过先引导用户补齐,再继续装配;选「暂不归属」则跳过,后续仍可随时归属。
17
+ 3. 对已有仓库优先运行 `dta agent enhance --project-name <name> --role-skill <role> --dry-run --json`。它只生成 `agent-enhancement-plan@1`,不会写文件、访问 DWS 或创建 Trigger。审阅 operations、blockers semanticReview 后,才复制计划给出的命令,用同一组参数、当前 `planId` `--yes` 落盘。
18
+ 4. apply 只允许本地文件副作用:先把被更新的旧文件备份到 `.dingtalk-agent/backups/agent-enhance/<operationId>/`,再写入并按 hash 回读;自定义 private state 目录必须同步进入 `.gitignore`。输入漂移、planId 过期、非法 Role 路径、未知 OpenCode instruction、路径越界或 symlink 都必须 fail closed。不要跳过 plan,也不要把 `--yes` 写进无人审阅的默认脚本。
19
+ 5. 审核本体:优先使用 `AGENTS.md` 表达长期身份、职责、交付物、拒绝边界和协作方式。CLI 只补骨架,绝不虚构这些语义;只要 `AGENTS.md` Role Skill 仍含模板 `<...>`,`agent audit` 必须保持 `partial`。
20
+ 6. 审核能力:Basic Behavior 是所有钉钉员工共享且**每个 Session 必须加载**的协议;岗位知识和流程拆到独立 Role/Workflow Skill。目录可发现不等于正文已加载,必须为目标 Agent Host 生成可验证的加载合同。不要把 FDE、招聘、事故处理等岗位方法写回 Basic Skill。
21
+ 7. 审核存储:明确工作记忆、业务事实、长期知识、产物和宿主私有控制状态分别去哪。介质可以换,语义层与控制状态不能混。远端 memory/knowledge plan 必须显式绑定 profile expected user,但 enhance 本身仍不读写远端。
22
+ 8. 运行 `dta bootstrap --bindings agent.bindings.json --json` 和 `dta agent audit --bindings agent.bindings.json --require-skill <role> --json`。静态配置和真实语义通过后再加 `--verify-load --yes`;不能把“写出了文件”或“目录存在”当成装配完成。
22
23
 
23
24
  ## Agent Host 加载合同
24
25
 
@@ -36,6 +37,15 @@ Why:OpenCode 会发现 `.agents/skills/<name>/SKILL.md`,但 Skill 正文默
36
37
 
37
38
  任何 `ready` 结论都必须保留以下证据:Basic Skill 的 name/version/hash、Host resolved config、项目内发现路径和一次随机 load probe。`agent-audit@1` 会把 Definition、Storage、Host exposure 与 load gate 分开报告;load gate 失败时,后续回答再像员工也只能算本体 Prompt 命中,不能算 Skill 生效。
38
39
 
40
+ ## 连接钉钉机器人的两条路径
41
+
42
+ 装配通过 audit `ready` 后,把 Agent 接到钉钉聊天有两条路径,必须呈现给用户选择:
43
+
44
+ - **本地调试(不需要托管平台)**:直接在宿主(OpenCode 等)里基于 `AGENTS.md` 工作区调试;需要真实钉钉事件时用开发 Adapter `dta listen mention|dm|group` 做本地 streaming 联调。适合开发期验证行为,不适合常驻服务。
45
+ - **发布到 Multica 托管平台(推荐正式使用)**:归属 `multica-dingtalk` 后,用平台技能包 `multica-fde-external`(`python3 scripts/multica_ext.py <命令>`)完成完整交付链——`workspace-create/workspace-init` 供给工作区 → `runtime-templates`/`agent-create` 供给运行时与 Agent → `agent-source-sync`/`skill-push` 同步本体与 Skill → `dingtalk-begin`/`dingtalk-bind` 绑定钉钉机器人(真实绑定需要真实 AppKey/AppSecret 或扫码,把返回的绑定链接交给用户完成)→ `chat-send --wait` 免钉钉直聊测试通道验收 → `task-trace --follow` 观测执行轨迹 → `agent-check` 综合体检。绑定完成后用户在钉钉向机器人发消息即可到达该 Agent。
46
+
47
+ 不要在未询问用户的情况下用 `dws dev app robot submit` 直接创建新的钉钉机器人应用——那是脱离托管平台的裸应用,消息事件不会到达任何 Agent 运行时;只有用户明确要自管事件链路时才走该路径。
48
+
39
49
  ## 可复制装配命令
40
50
 
41
51
  ```bash
@@ -0,0 +1,280 @@
1
+ ---
2
+ name: multica-fde-external
3
+ description: External Python wrapper defining the full developer chain for delivering an agent on the Multica (DingTalk-FDE fork) platform — workspace bootstrap, runtime/agent provisioning, skill assembly (push/pull), DingTalk robot & account binding, execution-trace observability, cron scheduling (autopilot), and health checks. Use when provisioning, operating, debugging, or observing a Multica-managed agent from the command line with only an endpoint and a token.
4
+ ---
5
+
6
+ # Multica FDE External Skill
7
+
8
+ This skill is the **Multica adapter** of the Managed Agent Platform developer
9
+ chain: everything a developer needs to take an agent from nothing to a
10
+ customer-facing DingTalk bot, then observe, schedule, and health-check it.
11
+ Multica may be only one of several managed platforms; this skill defines
12
+ exactly which capabilities the Multica backend provides and wraps each one as
13
+ a script command. An external orchestrator can fetch this skill (see
14
+ *Skill logistics*) and execute its scripts.
15
+
16
+ Deliberately **decoupled from the Go CLI**: nothing patches `multica`;
17
+ everything is plain HTTPS against the server. Only two inputs are needed —
18
+ **server URL** and **token** — both reusable from the CLI's stored config.
19
+
20
+ - Script: `scripts/multica_ext.py` — single file, stdlib only, Python >= 3.9.
21
+ - Proxy-immune: the script ignores `HTTP(S)_PROXY` env vars by design (unlike
22
+ curl or the Go CLI), so an exported shell proxy cannot break pre/intranet
23
+ endpoints.
24
+ - All commands print JSON to stdout (status notes on stderr) — pipe to `jq`.
25
+ - Global flags may appear before or after the command.
26
+
27
+ ## Credentials
28
+
29
+ Resolution order (first hit wins):
30
+
31
+ 1. Flags: `--server-url`, `--token`, `--workspace <slug|uuid>`
32
+ 2. Env: `MULTICA_SERVER_URL`, `MULTICA_TOKEN`, `MULTICA_WORKSPACE_ID`
33
+ 3. `--profile <name>` → `~/.multica/profiles/<name>/config.json`
34
+ 4. `~/.multica/config.json` (keys: `server_url`, `workspace_id`, `token`)
35
+
36
+ The token is a `mul_` personal access token or login JWT; mint PATs with
37
+ `POST /api/tokens`.
38
+
39
+ ```bash
40
+ PY=".agents/skills/multica-fde-external/scripts/multica_ext.py"
41
+ python3 $PY --help
42
+ python3 $PY whoami --profile pre-fde
43
+ ```
44
+
45
+ ## The developer chain
46
+
47
+ ### 1 — Provision: workspace, runtime, agent
48
+
49
+ ```bash
50
+ python3 $PY workspace-init --profile pre-fde --name "FDE 团队" --slug fde-team \
51
+ --repo https://code.example.com/org/repo.git --complete-onboarding
52
+ python3 $PY runtime-create-fc --workspace fde-team --template-id <id> # cloud FC-E2B runtime
53
+ python3 $PY agent-create --workspace fde-team --name "FDE 教练" \
54
+ --instructions "..." --model claude-sonnet-5 # sole runtime auto-picked
55
+ ```
56
+
57
+ `workspace-init` is idempotent (409 → reuse when you are a member); repos
58
+ merge, never clobber; repo registration needs owner/admin. Slugs:
59
+ `^[a-z0-9]+(-[a-z0-9]+)*$`, reserved list enforced. An agent needs a runtime:
60
+ FC-E2B cloud (`runtime-create-fc`, requires server-side FC-E2B enablement) or
61
+ a local daemon. `runtime-templates` lists templates; when `--template-id` is
62
+ omitted and exactly one exists it is auto-picked.
63
+
64
+ ### 2 — Define & assemble: where the agent comes from, what skills it has
65
+
66
+ - `agent-source --agent <uuid>` — which git repo backs the agent (the
67
+ per-agent `agent_sources` link: `github` or `managed_git`; `null` = not
68
+ git-backed). `agent-source-sync` re-syncs github-sourced agents
69
+ (`managed_git` refuses with 409 by design — updated by the platform).
70
+ - Skills: `skill-list`, `skill-get`, `skill-push --dir <path>`
71
+ (local directory → workspace, `--on-conflict overwrite` replaces content
72
+ AND files wholesale), `skill-pull --skill <id-or-name> --dest <dir>`
73
+ (workspace → local directory). Assign skills to an agent with the Go CLI
74
+ (`multica agent skills add`) or `PUT /api/agents/{id}/skills`.
75
+ - Gap to know: there is no API to bind an *arbitrary* git URL to an agent;
76
+ per-agent git links are created only by the GitHub publish flow or the
77
+ managed FDE provision flow. Workspace-level repos (`workspace-repos`) and
78
+ the two flows above are the supported paths.
79
+
80
+ ### 3 — Deliver: bind the agent to a customer-facing DingTalk bot
81
+
82
+ The binding is a **channel installation** (one robot app per agent), not a
83
+ Skill object. AppKey/AppSecret are `client_id`/`client_secret` on the wire;
84
+ the secret is encrypted at rest (`MULTICA_DINGTALK_SECRET_KEY`) and never
85
+ returned. Inbound messages use DingTalk Stream Mode — no public webhook
86
+ registration needed.
87
+
88
+ ```bash
89
+ # Self-service: inject an existing robot app's credentials (owner/admin)
90
+ python3 $PY dingtalk-bind --agent <uuid> --client-id <AppKey> --client-secret-stdin < secret.txt
91
+ # Or scan-to-create: DingTalk mints the app, server captures credentials
92
+ python3 $PY dingtalk-begin --agent <uuid> --wait
93
+ python3 $PY dingtalk-list ; python3 $PY dingtalk-revoke --installation <uuid>
94
+ # Personal-account binding family (dingtalk_account):
95
+ python3 $PY dingtalk-account-begin --agent <uuid> # prints QR URL
96
+ python3 $PY dingtalk-account-list ; python3 $PY dingtalk-account-unbind --installation <uuid>
97
+ ```
98
+
99
+ Bind and unbind always come in pairs: robot `dingtalk-bind`/`dingtalk-begin`
100
+ ↔ `dingtalk-revoke`; account `dingtalk-account-begin` ↔
101
+ `dingtalk-account-unbind`. `dingtalk-account-begin` returns the binding link
102
+ (`qr_code_url`) directly — hand it to the counterpart to scan; check the
103
+ result with `dingtalk-account-list` (`dws_identity` carries the bound
104
+ account's org/display name).
105
+
106
+ Server rules: one robot per agent; re-binding the same `client_id` **moves**
107
+ the installation; cross-workspace `client_id` reuse → 409. Error map for
108
+ `dingtalk-bind`: **502** "dingtalk credentials check failed" = wrong
109
+ AppKey/AppSecret; **409** = ownership/agent conflict; **404** = agent not in
110
+ this workspace; `configured: false` in `dingtalk-list` = server lacks the
111
+ secret key. Deletes are audit-kept (`status: revoked`).
112
+
113
+ ### 4 — Observe: the agent's working and reasoning process
114
+
115
+ The full step-by-step trace (reasoning text, every tool call with input JSON,
116
+ every tool result, errors) is **persisted forever** in `task_messages` and
117
+ retrievable by REST — no backend changes needed:
118
+
119
+ ```bash
120
+ python3 $PY agent-tasks --agent <uuid> # run history (status, error, failure_reason, kind)
121
+ python3 $PY task-trace --task <uuid> --text # full reasoning/tool trace, human-readable
122
+ python3 $PY task-trace --task <uuid> --follow --text # live: polls until the task ends
123
+ python3 $PY issue-tasks --issue FDE-1 # all runs for an issue (uuid or identifier)
124
+ python3 $PY issue-timeline --issue FDE-1 # comments + activity log merged (status changes, task_completed...)
125
+ python3 $PY issue-usage --issue FDE-1 # aggregated token usage
126
+ python3 $PY task-snapshot # workspace-wide: active tasks + each agent's latest outcome
127
+ ```
128
+
129
+ Chat surfaces are REST-pollable too (`/api/chat/sessions/...` — session
130
+ messages, `pending-task`); realtime push is WebSocket `/ws` only (no SSE),
131
+ but a poll-only client fully reconstructs any past run from `task-trace`.
132
+
133
+ ### 5 — Schedule: cron automations (autopilot)
134
+
135
+ ```bash
136
+ python3 $PY autopilot-create --title "每日巡检" --agent <uuid> \
137
+ --description "<the prompt for each run>" --cron "0 9 * * 1-5" --timezone Asia/Shanghai
138
+ python3 $PY autopilot-trigger --autopilot <uuid> # run once, now
139
+ python3 $PY autopilot-runs --autopilot <uuid> # history: status, linked issue/task
140
+ python3 $PY autopilot-set-status --autopilot <uuid> --status paused # pause / active
141
+ python3 $PY autopilot-list ; python3 $PY autopilot-get --autopilot <uuid>
142
+ ```
143
+
144
+ Cron is standard 5-field (no seconds, no `@daily`), IANA timezone (default
145
+ UTC in the API; this script defaults `Asia/Shanghai`). `create_issue` mode
146
+ creates an issue per run (title template supports `{{date}}`) and enqueues an
147
+ agent task; `run_only` enqueues a task with no issue. Runs are idempotent per
148
+ (trigger, planned time); missed fires collapse to the latest, >5 min late is
149
+ skipped. Webhook triggers also exist (`POST /api/autopilots/{id}/triggers`
150
+ with `kind: webhook`).
151
+
152
+ ### 6 — Check: doctor report
153
+
154
+ ```bash
155
+ python3 $PY agent-check --agent <uuid>
156
+ ```
157
+
158
+ Composite JSON report: agent status/archived, runtime health (online /
159
+ unstable = offline but seen <5 min ago / offline / missing), active tasks +
160
+ last outcome (with `failure_reason`), recent failure count, DingTalk robot +
161
+ account binding state, plus a `checks[]` verdict list; `healthy` = no `fail`
162
+ (warns don't fail the check).
163
+
164
+ ### 7 — Basics: issues, comments, chat, members, tokens
165
+
166
+ Everyday Multica operations so an orchestrator needs nothing but this script:
167
+
168
+ ```bash
169
+ python3 $PY issue-create --title "..." --assignee-agent <uuid> # assigning an agent triggers a run
170
+ python3 $PY issue-get --issue FDE-1 ; python3 $PY issue-list --open-only --assignee <uuid>
171
+ python3 $PY issue-update --issue FDE-1 --status done
172
+ python3 $PY comment-add --issue FDE-1 --content "..." # "/note " prefix suppresses agent triggering
173
+ python3 $PY comment-list --issue FDE-1 --recent 10
174
+ python3 $PY chat-send --agent <uuid> --content "你好" --wait # direct conversation — the no-DingTalk e2e test path
175
+ python3 $PY member-list ; python3 $PY member-invite --email a@b.c --role member
176
+ python3 $PY token-create --name ci-bot --expires-days 90 # plaintext token shown once
177
+ ```
178
+
179
+ `chat-send` creates a session when `--session` is omitted and with `--wait`
180
+ polls `pending-task` until the assistant reply lands — the fastest way to
181
+ smoke-test a freshly provisioned agent before binding any channel.
182
+
183
+ ## Skill logistics: how skills move (the three planes)
184
+
185
+ - **Up (local → workspace):** `skill-push --dir` (this script);
186
+ `multica skill import --url|--file` (URL / `.skill`/`.zip` archive);
187
+ or the runtime *local-skills* API — the daemon scans the machine's
188
+ home-level skill dirs (`~/.claude/skills`, `~/.codex/skills`, the
189
+ cross-tool universal root `~/.agents/skills`, Claude plugin roots, etc.;
190
+ a "skill" = a directory with `SKILL.md`) and the workspace can list and
191
+ import them (`POST /api/runtimes/{id}/local-skills[/import]`, owner-only
192
+ for import).
193
+ - **Store:** workspace DB (`content` = SKILL.md, `files[]` = supporting
194
+ files; `SKILL.md` is a reserved file path).
195
+ - **Down (workspace → local):** automatic — the daemon materializes assigned
196
+ skills into each task's exec env (per-provider dirs, e.g.
197
+ `.claude/skills`, ephemeral, task-scoped). Manual/persistent — the Go CLI
198
+ has **no** download command; `skill-pull` (this script) is the downlink:
199
+ it writes `SKILL.md` + files into a local directory, byte-identical to
200
+ what was pushed. An external orchestrator can therefore
201
+ `skill-pull --skill multica-fde-external --dest <dir>` and execute the
202
+ scripts it receives.
203
+
204
+ ## Command → API map
205
+
206
+ | Command | API |
207
+ | --- | --- |
208
+ | `whoami` | `GET /api/me` |
209
+ | `workspace-list` / `workspace-create` / `workspace-init` | `GET`/`POST /api/workspaces` (+ `PUT /api/workspaces/{id}`, `POST /api/me/onboarding/complete`) |
210
+ | `workspace-repos` | `GET`/`PUT /api/workspaces/{id}` (`repos` JSONB) |
211
+ | `runtime-list` / `runtime-templates` / `runtime-create-fc` | `GET /api/runtimes`, `GET`/`POST /api/runtimes/fc-e2b(/templates)` |
212
+ | `agent-list` / `agent-get` / `agent-create` | `GET`/`POST /api/agents` |
213
+ | `agent-source` / `agent-source-sync` | `GET /api/agents/{id}/source`, `POST .../source/sync` |
214
+ | `agent-tasks` | `GET /api/agents/{id}/tasks` |
215
+ | `task-trace` | `GET /api/tasks/{taskId}/messages?since=` (+ `GET /api/agent-task-snapshot` for follow-stop) |
216
+ | `issue-tasks` / `issue-timeline` / `issue-usage` | `GET /api/issues/{id}/task-runs` / `/timeline` / `/usage` |
217
+ | `task-snapshot` | `GET /api/agent-task-snapshot` |
218
+ | `autopilot-*` | `GET`/`POST`/`PATCH /api/autopilots`, `POST .../trigger`, `GET .../runs`, `POST .../triggers` |
219
+ | `agent-check` | composite of agents/runtimes/snapshot/tasks/dingtalk endpoints |
220
+ | `dingtalk-bind` / `dingtalk-begin` | `POST /api/workspaces/{id}/dingtalk/install/manual` / `install/begin` + status poll |
221
+ | `dingtalk-list` / `dingtalk-revoke` | `GET`/`DELETE .../dingtalk/installations` |
222
+ | `dingtalk-account-*` | `POST`/`GET`/`DELETE .../dingtalk/account-bindings` |
223
+ | `skill-list` / `skill-get` / `skill-pull` / `skill-push` | `GET /api/skills(/{id})`, `POST /api/skills`, `PUT /api/skills/{id}` |
224
+ | `issue-create` / `issue-get` / `issue-list` / `issue-update` | `POST`/`GET`/`PUT /api/issues(/{id})` |
225
+ | `comment-add` / `comment-list` | `POST`/`GET /api/issues/{id}/comments` |
226
+ | `chat-send` | `POST /api/chat/sessions(/{id}/messages)` + `GET .../pending-task` + `GET .../messages` |
227
+ | `member-list` / `member-invite` | `GET`/`POST /api/workspaces/{id}/members` |
228
+ | `token-create` | `POST /api/tokens` |
229
+
230
+ Auth: `Authorization: Bearer <token>`; workspace-scoped generic routes use
231
+ the `X-Workspace-ID` header, `/api/workspaces/{id}/...` routes take the UUID
232
+ in the path. No cookies, no CSRF.
233
+
234
+ ## Distribution
235
+
236
+ This directory is self-contained (SKILL.md + scripts):
237
+
238
+ - Import into any Multica workspace: `multica skill import` from a URL or
239
+ `.skill`/`.zip` archive, or `skill-push` from this script. Files land in
240
+ agent envs without the exec bit — always invoke via
241
+ `python3 .../multica_ext.py` / `sh .../bootstrap.sh`, never `./x`.
242
+ - Or vendor into a dedicated git repo later; nothing depends on this
243
+ monorepo.
244
+
245
+ ### Bootstrap: getting this skill onto a fresh machine
246
+
247
+ The workspace itself is the registry — no extra storage needed. `skill-pull`
248
+ lives inside the skill, so the first fetch uses `scripts/bootstrap.sh`
249
+ (copy it from a runbook, teammate, or this repo):
250
+
251
+ ```bash
252
+ # Path A — authenticated multica CLI (no pull command needed: skill get
253
+ # outputs everything; bootstrap just writes it to disk):
254
+ sh bootstrap.sh --profile pre-fde --workspace-id <uuid> [--dest <dir>]
255
+
256
+ # Path B — no CLI at all, just endpoint + token (+ curl, python3):
257
+ MULTICA_SERVER_URL=... MULTICA_TOKEN=... MULTICA_WORKSPACE_ID=... \
258
+ sh bootstrap.sh --curl [--dest <dir>]
259
+ ```
260
+
261
+ Both paths produce a byte-identical copy of what `skill-push` uploaded, and
262
+ bootstrap.sh ships inside the bundle, so a pulled copy can bootstrap the
263
+ next machine. After bootstrap, stay current with
264
+ `python3 scripts/multica_ext.py skill-pull --skill multica-fde-external --dest . --force`.
265
+
266
+ Equivalent one-liner where bootstrap.sh is unavailable:
267
+
268
+ ```bash
269
+ multica skill get <skill-id> --output json | python3 -c '
270
+ import json,sys,pathlib
271
+ sk=json.load(sys.stdin); d=pathlib.Path(sk["name"]); d.mkdir(exist_ok=True)
272
+ (d/"SKILL.md").write_text(sk.get("content") or "")
273
+ for f in sk.get("files") or []:
274
+ p=d/f["path"]; p.parent.mkdir(parents=True,exist_ok=True); p.write_text(f.get("content") or "")'
275
+ ```
276
+
277
+ OSS (or any object store) is an optional *anonymous* channel — useful only
278
+ when the target machine has no Multica credentials yet. It needs a publish
279
+ step on every update and bucket/ACL management, so prefer the
280
+ workspace-as-registry paths above when a token exists.