feat: bundle desktop skills
This commit is contained in:
@@ -12,12 +12,21 @@
|
|||||||
- placeholder submit status/result for media modes
|
- placeholder submit status/result for media modes
|
||||||
3. Extend browser preview mock skill commands so install/list/toggle reflects the JianYing skill integration.
|
3. Extend browser preview mock skill commands so install/list/toggle reflects the JianYing skill integration.
|
||||||
4. Add focused CSS to `desktop/src/index.css`, using restrained desktop styling and avoiding global side effects.
|
4. Add focused CSS to `desktop/src/index.css`, using restrained desktop styling and avoiding global side effects.
|
||||||
5. Run validation:
|
5. Bundle the curated Desktop skill pack in `desktop/src-tauri/builtin-skills`, seed it into `~/.boosapi/skills`, and expose it through marketplace fallback results:
|
||||||
|
- JianYing editor automation
|
||||||
|
- Seedance 2 prompt/video skills
|
||||||
|
- DWG MCP server wrapper
|
||||||
|
- CAD AI agent wrapper
|
||||||
|
- Gorden PPT skill
|
||||||
|
6. Run validation:
|
||||||
- `bun run typecheck`
|
- `bun run typecheck`
|
||||||
- `bun run build`
|
- `bun run build`
|
||||||
6. Review diff for unrelated churn before final report.
|
7. Review diff for unrelated churn before final report.
|
||||||
|
|
||||||
## Validation
|
## Validation
|
||||||
|
|
||||||
- `bun run typecheck` passed in `/root/metapi/desktop` after JianYing skill integration.
|
- `bun run typecheck` passed in `/root/metapi/desktop` after JianYing skill integration.
|
||||||
- `bun run build` passed in `/root/metapi/desktop` after JianYing skill integration.
|
- `bun run build` passed in `/root/metapi/desktop` after JianYing skill integration.
|
||||||
|
- `pnpm --dir desktop exec tsc --noEmit` passed after bundling built-in skills.
|
||||||
|
- `pnpm --dir desktop run build` passed after bundling built-in skills.
|
||||||
|
- `cargo check -p open-agent-sdk --manifest-path desktop/src-tauri/Cargo.toml` passed after bundling built-in skills.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Add a first usable Agent Workbench entry to the desktop client so users can star
|
|||||||
- The page must follow the desktop app’s existing React/Ant Design/Zustand conventions and be reachable from the sidebar.
|
- The page must follow the desktop app’s existing React/Ant Design/Zustand conventions and be reachable from the sidebar.
|
||||||
- The implementation must avoid new backend schema. Existing Tauri commands may be reused.
|
- The implementation must avoid new backend schema. Existing Tauri commands may be reused.
|
||||||
- The Workbench skills menu should expose a JianYing/剪映 automation entry that installs/enables `luoluoluo22/jianying-editor-skill` through the existing Desktop skills system and primes the Agent prompt.
|
- The Workbench skills menu should expose a JianYing/剪映 automation entry that installs/enables `luoluoluo22/jianying-editor-skill` through the existing Desktop skills system and primes the Agent prompt.
|
||||||
|
- Desktop should bundle the first-party curated skill pack in the project so JianYing, Seedance, DWG/CAD, and PPT skills are available even when the online `skills.sh` market does not return them.
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ Add a first usable Agent Workbench entry to the desktop client so users can star
|
|||||||
- [x] Local file selection displays attachment chips and supports removing them.
|
- [x] Local file selection displays attachment chips and supports removing them.
|
||||||
- [x] Agent mode creates an Agent conversation/session and sends the prompt plus attachments through the existing Agent stream flow.
|
- [x] Agent mode creates an Agent conversation/session and sends the prompt plus attachments through the existing Agent stream flow.
|
||||||
- [x] Skills menu can install/enable the JianYing editor skill and insert a matching Agent prompt prefix.
|
- [x] Skills menu can install/enable the JianYing editor skill and insert a matching Agent prompt prefix.
|
||||||
|
- [x] Desktop seeds bundled skills into the local skills directory and mirrors them in the marketplace/browser preview.
|
||||||
- [x] Unsupported submit paths do not crash; they show a clear in-page result/status.
|
- [x] Unsupported submit paths do not crash; they show a clear in-page result/status.
|
||||||
- [x] `bun run typecheck` and `bun run build` pass in `/root/metapi/desktop`.
|
- [x] `bun run typecheck` and `bun run build` pass in `/root/metapi/desktop`.
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,38 @@
|
|||||||
"source": "vercel-labs/agent-skills",
|
"source": "vercel-labs/agent-skills",
|
||||||
"sourceType": "github",
|
"sourceType": "github",
|
||||||
"computedHash": "a6a44d5498f7e8f68289902f3dedfc6f38ae0cee1e96527c80724cf27f727c2a"
|
"computedHash": "a6a44d5498f7e8f68289902f3dedfc6f38ae0cee1e96527c80724cf27f727c2a"
|
||||||
|
},
|
||||||
|
"jianying-editor": {
|
||||||
|
"source": "luoluoluo22/jianying-editor-skill",
|
||||||
|
"sourceType": "github",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"seedance": {
|
||||||
|
"source": "MapleShaw/seedance2.0-prompt-skill",
|
||||||
|
"sourceType": "github",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"seedance-prompt-en": {
|
||||||
|
"source": "dexhunter/seedance2-skill",
|
||||||
|
"sourceType": "github",
|
||||||
|
"bundled": true
|
||||||
|
},
|
||||||
|
"dwg-mcp-server": {
|
||||||
|
"source": "dimitrovakulenko/dwg-mcp-server",
|
||||||
|
"sourceType": "github",
|
||||||
|
"bundled": true,
|
||||||
|
"wrapper": true
|
||||||
|
},
|
||||||
|
"cad-ai-agent": {
|
||||||
|
"source": "jeremylongshore/cad-ai-agent",
|
||||||
|
"sourceType": "github",
|
||||||
|
"bundled": true,
|
||||||
|
"wrapper": true
|
||||||
|
},
|
||||||
|
"gorden-ppt-skill": {
|
||||||
|
"source": "GordenSun/GordenPPTSkill",
|
||||||
|
"sourceType": "github",
|
||||||
|
"bundled": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1
@@ -1184,6 +1184,7 @@ dependencies = [
|
|||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-autostart",
|
"tauri-plugin-autostart",
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ tauri-plugin-process = "2"
|
|||||||
tauri-plugin-updater = "2"
|
tauri-plugin-updater = "2"
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
serde_yaml = "0.9"
|
||||||
boosapi-core = { path = "crates/core" }
|
boosapi-core = { path = "crates/core" }
|
||||||
boosapi-providers = { path = "crates/providers" }
|
boosapi-providers = { path = "crates/providers" }
|
||||||
boosapi-agent = { path = "crates/agent" }
|
boosapi-agent = { path = "crates/agent" }
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
name: cad-ai-agent
|
||||||
|
description: CAD/DXF drawing intelligence workflow for summaries, compliance checks, quantity takeoffs, RFIs, health reports, revision comparison, and safe structured edit planning.
|
||||||
|
---
|
||||||
|
|
||||||
|
# CAD AI Agent Skill
|
||||||
|
|
||||||
|
Use this skill when a user asks for CAD/DXF drawing intelligence: summaries, compliance checks, quantity takeoffs, RFIs, health reports, revision comparison, or safe edit planning.
|
||||||
|
|
||||||
|
Upstream project: https://github.com/jeremylongshore/cad-ai-agent
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Clone and install the upstream project when the user wants to run the full local pipeline:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/jeremylongshore/cad-ai-agent.git
|
||||||
|
cd cad-ai-agent
|
||||||
|
python -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install -e ".[dev]"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Identify the drawing format: DXF is directly supported by the upstream project; DWG/PDF may need conversion or a different inspection pipeline.
|
||||||
|
2. Ask what the user wants: summarize, edit, compare, query, generate documentation, compliance, coordination, estimation, or quality.
|
||||||
|
3. Preserve originals. Prefer save-as outputs and structured reports.
|
||||||
|
4. For edits, produce structured operation plans rather than modifying raw CAD data directly.
|
||||||
|
5. For analysis, return layer/entity summaries, issues, measurements, and concrete next actions.
|
||||||
|
|
||||||
|
This bundled skill is a BoosAPI Desktop wrapper around the upstream project and keeps CAD operations explicit and reversible.
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
name: dwg-mcp-server
|
||||||
|
description: Inspect AutoCAD DWG files through DWG MCP Server. Use when users ask to open, inspect, query, summarize, or extract objects/properties from DWG drawings.
|
||||||
|
---
|
||||||
|
|
||||||
|
# DWG MCP Server Skill
|
||||||
|
|
||||||
|
Use this skill when a user wants to inspect AutoCAD DWG drawings with an AI agent.
|
||||||
|
|
||||||
|
Upstream project: https://github.com/dimitrovakulenko/dwg-mcp-server
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Install the MCP server in your MCP client or run it with npx:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx -y @dmytro-prototypes/dwg-mcp-server
|
||||||
|
```
|
||||||
|
|
||||||
|
For clients without MCP roots, configure explicit allowed roots:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DWG_MCP_ALLOWED_ROOTS="$HOME" npx -y @dmytro-prototypes/dwg-mcp-server
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Ask the user for the DWG file path and confirm it is under an allowed root.
|
||||||
|
2. Use `dwg.list_roots` to inspect accessible folders.
|
||||||
|
3. Use `dwg.open_file` to open the drawing and get a `documentId`.
|
||||||
|
4. Use `dwg.list_file_types`, `dwg.describe_type`, `dwg.get_objects`, and `dwg.query_objects` to inspect contents.
|
||||||
|
5. Summarize entities, layers, blocks, layouts, references, and notable properties.
|
||||||
|
6. Close the document with `dwg.close_file` when done.
|
||||||
|
|
||||||
|
This bundled skill is a BoosAPI Desktop wrapper around the upstream MCP server. It does not edit DWG files directly.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: gorden-ppt-skill
|
||||||
|
description: 中文 PPT 生成与编辑 Skill,支持工作汇报、年终总结、述职竞聘、项目复盘、商务提案、教学课件、模板套用、版式保持和 PowerPoint 成品导出。
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gorden PPT Skill
|
||||||
|
|
||||||
|
Use this skill when the user wants to create or edit a PowerPoint / PPT / slides / `.pptx` deck.
|
||||||
|
|
||||||
|
Upstream full skill: https://github.com/GordenSun/GordenPPTSkill
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Clarify deck purpose, audience, language, page count, format, and whether the user has a template.
|
||||||
|
2. If using a template, preserve layout, typography, colors, and spacing; replace content without damaging the design.
|
||||||
|
3. If creating from scratch, propose a concise structure first: cover, agenda, context, key points, data pages, conclusion, and appendix as needed.
|
||||||
|
4. Keep slide text short and presentation-ready.
|
||||||
|
5. Check text overflow, same-level heading consistency, and visual hierarchy before final export.
|
||||||
|
|
||||||
|
Install `GordenSun/GordenPPTSkill` from the Skills marketplace for the full template library and update scripts.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: jianying-editor
|
||||||
|
description: 剪映 AI 自动化剪辑 Skill,支持草稿生成、素材导入、字幕、配音、特效、转场、录屏和导出规划。Use when the user wants to automate JianYing/CapCut editing, create a draft, assemble clips, add captions, voiceover, music, transitions, or export a video.
|
||||||
|
---
|
||||||
|
|
||||||
|
# JianYing Editor Skill
|
||||||
|
|
||||||
|
Use this skill when the user wants to automate video editing with JianYing/CapCut.
|
||||||
|
|
||||||
|
Upstream full skill: https://github.com/luoluoluo22/jianying-editor-skill
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Clarify the target: platform, duration, aspect ratio, source assets, narration, subtitle style, music, and export format.
|
||||||
|
2. Build an edit plan before scripting: timeline structure, clips, captions, voiceover, music, effects, transitions, and export path.
|
||||||
|
3. Keep generated scripts in the user's project/workspace, not inside the skill directory.
|
||||||
|
4. Preserve original media. Generate a new draft/project for each requested edit.
|
||||||
|
5. When the full upstream skill is installed, follow its detailed `rules/` and `docs/agent-playbook.md`.
|
||||||
|
|
||||||
|
## Install Full Upstream Version
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/luoluoluo22/jianying-editor-skill.git
|
||||||
|
```
|
||||||
|
|
||||||
|
In BoosAPI Desktop, install `luoluoluo22/jianying-editor-skill` from the Skills marketplace to replace this bundled quick-start with the full implementation.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: seedance-prompt-en
|
||||||
|
description: English Seedance 2.0 multimodal AI video prompt guide. Use for text/image/video/audio reference prompts with @Image, @Video, and @Audio syntax.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Seedance 2.0 English Prompt Skill
|
||||||
|
|
||||||
|
Use this skill when the user wants English prompts for Jimeng / Seedance 2.0 multimodal AI video generation.
|
||||||
|
|
||||||
|
Upstream full skill: https://github.com/dexhunter/seedance2-skill
|
||||||
|
|
||||||
|
## Reference Syntax
|
||||||
|
|
||||||
|
Use explicit reference roles:
|
||||||
|
|
||||||
|
- `@Image1 as the first frame`
|
||||||
|
- `@Image1's character as the subject`
|
||||||
|
- `scene references @Image2`
|
||||||
|
- `reference @Video1's camera movement`
|
||||||
|
- `BGM references @Audio1`
|
||||||
|
|
||||||
|
## Prompt Blueprint
|
||||||
|
|
||||||
|
```text
|
||||||
|
[Subject setup] + [Scene/environment] + [Action/motion] + [Camera movement] + [Style/lighting] + [Audio/sound] + [Constraints]
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep reference-heavy prompts precise. Say what each uploaded asset controls and what the model may invent.
|
||||||
|
|
||||||
|
Install `dexhunter/seedance2-skill` from the Skills marketplace for the full guide.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
name: seedance
|
||||||
|
description: 中文 Seedance 2.0 / 即梦视频与图片提示词工程,支持 @素材引用、分镜、运镜、首帧图、图生视频、短剧、广告视频和长视频流水线。
|
||||||
|
---
|
||||||
|
|
||||||
|
# Seedance 2.0 中文提示词 Skill
|
||||||
|
|
||||||
|
Use this skill when the user asks for 即梦 / Seedance / Dreamina video prompts, image-to-video prompts, storyboard prompts, first-frame image prompts, camera motion, short video ads, short drama clips, or multimodal reference prompts.
|
||||||
|
|
||||||
|
Upstream full skill: https://github.com/MapleShaw/seedance2.0-prompt-skill
|
||||||
|
|
||||||
|
## Core Rules
|
||||||
|
|
||||||
|
1. Write prompts in Chinese unless the user asks otherwise.
|
||||||
|
2. Use official reference syntax: `@图片1`-`@图片9`, `@视频1`-`@视频3`, `@音频1`-`@音频3`.
|
||||||
|
3. For uploaded images, explain the role of each reference: first frame, character, scene, product, action, camera, rhythm, or style.
|
||||||
|
4. For short clips under 15 seconds, focus on one impossible or highly attractive moment with a strong first two seconds.
|
||||||
|
5. For longer videos, split into character reference, storyboard, key frames, per-shot prompts, and editing instructions.
|
||||||
|
6. Avoid realistic identifiable human faces in uploaded references unless the platform and user confirm compliance.
|
||||||
|
|
||||||
|
## Prompt Shape
|
||||||
|
|
||||||
|
```text
|
||||||
|
[主体/角色] + [场景/光影/质感] + [动作节奏] + [运镜] + [镜头/景别] + [声音/情绪] + [硬约束]
|
||||||
|
```
|
||||||
|
|
||||||
|
For image-to-video, keep the text concise and only add what the image does not already specify.
|
||||||
|
|
||||||
|
Install `MapleShaw/seedance2.0-prompt-skill` from the Skills marketplace for the full reference library.
|
||||||
@@ -4,6 +4,73 @@ use boosapi_core::types::*;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use tauri::State;
|
use tauri::State;
|
||||||
|
|
||||||
|
struct BuiltinMarketplaceSkill {
|
||||||
|
name: &'static str,
|
||||||
|
description: &'static str,
|
||||||
|
repo: &'static str,
|
||||||
|
stars: i64,
|
||||||
|
installs: i64,
|
||||||
|
keywords: &'static [&'static str],
|
||||||
|
fallback_skill_name: Option<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const BUILTIN_MARKETPLACE_SKILLS: &[BuiltinMarketplaceSkill] = &[
|
||||||
|
BuiltinMarketplaceSkill {
|
||||||
|
name: "jianying-editor",
|
||||||
|
description: "剪映 AI 自动化剪辑 Skill,支持草稿生成、素材导入、字幕、配音、特效和导出。",
|
||||||
|
repo: "luoluoluo22/jianying-editor-skill",
|
||||||
|
stars: 0,
|
||||||
|
installs: 0,
|
||||||
|
keywords: &["jianying", "剪映", "capcut", "video", "editing", "视频", "剪辑", "boosapi"],
|
||||||
|
fallback_skill_name: None,
|
||||||
|
},
|
||||||
|
BuiltinMarketplaceSkill {
|
||||||
|
name: "seedance",
|
||||||
|
description: "中文 Seedance 2.0 / 即梦视频与图片提示词工程,支持 @素材引用、分镜、运镜和长视频流水线。",
|
||||||
|
repo: "MapleShaw/seedance2.0-prompt-skill",
|
||||||
|
stars: 0,
|
||||||
|
installs: 0,
|
||||||
|
keywords: &["seedance", "即梦", "jimeng", "video", "prompt", "视频", "提示词", "boosapi"],
|
||||||
|
fallback_skill_name: None,
|
||||||
|
},
|
||||||
|
BuiltinMarketplaceSkill {
|
||||||
|
name: "seedance-prompt-en",
|
||||||
|
description: "English Seedance 2.0 multimodal AI video prompt writing guide with @Image/@Video/@Audio reference syntax.",
|
||||||
|
repo: "dexhunter/seedance2-skill",
|
||||||
|
stars: 0,
|
||||||
|
installs: 0,
|
||||||
|
keywords: &["seedance", "jimeng", "dreamina", "video", "prompt", "english", "boosapi"],
|
||||||
|
fallback_skill_name: None,
|
||||||
|
},
|
||||||
|
BuiltinMarketplaceSkill {
|
||||||
|
name: "dwg-mcp-server",
|
||||||
|
description: "DWG MCP Server 包装技能:指导 Agent 安装并使用 dwg-mcp 检查 AutoCAD DWG 文件。",
|
||||||
|
repo: "dimitrovakulenko/dwg-mcp-server",
|
||||||
|
stars: 0,
|
||||||
|
installs: 0,
|
||||||
|
keywords: &["dwg", "cad", "autocad", "mcp", "drawing", "图纸", "建筑", "boosapi"],
|
||||||
|
fallback_skill_name: Some("dwg-mcp-server"),
|
||||||
|
},
|
||||||
|
BuiltinMarketplaceSkill {
|
||||||
|
name: "cad-ai-agent",
|
||||||
|
description: "CAD/DXF Agent 包装技能:用于 DXF/DWG/PDF 图纸摘要、合规检查、工程量统计、RFI 和安全编辑流程。",
|
||||||
|
repo: "jeremylongshore/cad-ai-agent",
|
||||||
|
stars: 0,
|
||||||
|
installs: 0,
|
||||||
|
keywords: &["cad", "dxf", "dwg", "rfi", "takeoff", "compliance", "drawing", "图纸", "boosapi"],
|
||||||
|
fallback_skill_name: Some("cad-ai-agent"),
|
||||||
|
},
|
||||||
|
BuiltinMarketplaceSkill {
|
||||||
|
name: "gorden-ppt-skill",
|
||||||
|
description: "中文 PPT 生成与编辑 Skill,内置模板、版式保持、出框检测和 PowerPoint 成品导出流程。",
|
||||||
|
repo: "GordenSun/GordenPPTSkill",
|
||||||
|
stars: 0,
|
||||||
|
installs: 0,
|
||||||
|
keywords: &["ppt", "powerpoint", "slides", "演示", "幻灯片", "汇报", "boosapi"],
|
||||||
|
fallback_skill_name: None,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
fn home_dir() -> PathBuf {
|
fn home_dir() -> PathBuf {
|
||||||
dirs::home_dir().expect("Could not determine home directory")
|
dirs::home_dir().expect("Could not determine home directory")
|
||||||
}
|
}
|
||||||
@@ -12,8 +79,79 @@ fn skills_dir() -> PathBuf {
|
|||||||
boosapi_home().join("skills")
|
boosapi_home().join("skills")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BundledSkill {
|
||||||
|
name: &'static str,
|
||||||
|
source_ref: &'static str,
|
||||||
|
body: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
const BUNDLED_SKILLS: &[BundledSkill] = &[
|
||||||
|
BundledSkill {
|
||||||
|
name: "jianying-editor",
|
||||||
|
source_ref: "luoluoluo22/jianying-editor-skill",
|
||||||
|
body: include_str!("../../builtin-skills/jianying-editor/SKILL.md"),
|
||||||
|
},
|
||||||
|
BundledSkill {
|
||||||
|
name: "seedance",
|
||||||
|
source_ref: "MapleShaw/seedance2.0-prompt-skill",
|
||||||
|
body: include_str!("../../builtin-skills/seedance/SKILL.md"),
|
||||||
|
},
|
||||||
|
BundledSkill {
|
||||||
|
name: "seedance-prompt-en",
|
||||||
|
source_ref: "dexhunter/seedance2-skill",
|
||||||
|
body: include_str!("../../builtin-skills/seedance-prompt-en/SKILL.md"),
|
||||||
|
},
|
||||||
|
BundledSkill {
|
||||||
|
name: "dwg-mcp-server",
|
||||||
|
source_ref: "dimitrovakulenko/dwg-mcp-server",
|
||||||
|
body: include_str!("../../builtin-skills/dwg-mcp-server/SKILL.md"),
|
||||||
|
},
|
||||||
|
BundledSkill {
|
||||||
|
name: "cad-ai-agent",
|
||||||
|
source_ref: "jeremylongshore/cad-ai-agent",
|
||||||
|
body: include_str!("../../builtin-skills/cad-ai-agent/SKILL.md"),
|
||||||
|
},
|
||||||
|
BundledSkill {
|
||||||
|
name: "gorden-ppt-skill",
|
||||||
|
source_ref: "GordenSun/GordenPPTSkill",
|
||||||
|
body: include_str!("../../builtin-skills/gorden-ppt-skill/SKILL.md"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn seed_builtin_skills() -> Result<(), String> {
|
||||||
|
let target_root = skills_dir();
|
||||||
|
std::fs::create_dir_all(&target_root).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
for skill in BUNDLED_SKILLS {
|
||||||
|
let skill_dir = target_root.join(skill.name);
|
||||||
|
let skill_file = skill_dir.join("SKILL.md");
|
||||||
|
if skill_file.exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::create_dir_all(&skill_dir).map_err(|e| e.to_string())?;
|
||||||
|
std::fs::write(&skill_file, skill.body).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
let manifest = serde_json::json!({
|
||||||
|
"source_kind": "builtin",
|
||||||
|
"source_ref": skill.source_ref,
|
||||||
|
"installed_at": chrono::Utc::now().to_rfc3339(),
|
||||||
|
"installed_via": "boosapi-desktop"
|
||||||
|
});
|
||||||
|
std::fs::write(
|
||||||
|
skill_dir.join("skill-manifest.json"),
|
||||||
|
serde_json::to_string_pretty(&manifest).unwrap(),
|
||||||
|
)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn list_skills(state: State<'_, AppState>) -> Result<Vec<SkillInfo>, String> {
|
pub async fn list_skills(state: State<'_, AppState>) -> Result<Vec<SkillInfo>, String> {
|
||||||
|
seed_builtin_skills()?;
|
||||||
|
|
||||||
let home = home_dir();
|
let home = home_dir();
|
||||||
let skills = open_agent_sdk::skills::load_all_global(&home);
|
let skills = open_agent_sdk::skills::load_all_global(&home);
|
||||||
let disabled = boosapi_core::repo::skill::get_disabled_skills(&state.sea_db)
|
let disabled = boosapi_core::repo::skill::get_disabled_skills(&state.sea_db)
|
||||||
@@ -220,6 +358,8 @@ async fn install_from_github(owner: &str, repo: &str, target_dir: &Path) -> Resu
|
|||||||
}
|
}
|
||||||
|
|
||||||
copy_dir_recursive(&extracted, &skill_target)?;
|
copy_dir_recursive(&extracted, &skill_target)?;
|
||||||
|
let installed_name =
|
||||||
|
ensure_skill_entrypoint(&skill_target, owner, repo).unwrap_or_else(|| repo.to_string());
|
||||||
|
|
||||||
let manifest = serde_json::json!({
|
let manifest = serde_json::json!({
|
||||||
"source_kind": "github",
|
"source_kind": "github",
|
||||||
@@ -236,7 +376,45 @@ async fn install_from_github(owner: &str, repo: &str, target_dir: &Path) -> Resu
|
|||||||
)
|
)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
Ok(repo.to_string())
|
Ok(installed_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_skill_entrypoint(skill_target: &Path, owner: &str, repo: &str) -> Option<String> {
|
||||||
|
let existing_skill = skill_target.join("SKILL.md");
|
||||||
|
if existing_skill.exists() {
|
||||||
|
return read_skill_name(&existing_skill).or_else(|| Some(repo.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let source_ref = format!("{}/{}", owner, repo);
|
||||||
|
let builtin = BUILTIN_MARKETPLACE_SKILLS
|
||||||
|
.iter()
|
||||||
|
.find(|skill| skill.repo.eq_ignore_ascii_case(&source_ref))?;
|
||||||
|
let name = builtin.fallback_skill_name?;
|
||||||
|
let body = BUNDLED_SKILLS
|
||||||
|
.iter()
|
||||||
|
.find(|skill| skill.name == name)
|
||||||
|
.map(|skill| skill.body)?;
|
||||||
|
if std::fs::write(&existing_skill, body).is_ok() {
|
||||||
|
Some(name.to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_skill_name(skill_file: &Path) -> Option<String> {
|
||||||
|
let text = std::fs::read_to_string(skill_file).ok()?;
|
||||||
|
let trimmed = text.trim_start();
|
||||||
|
if !trimmed.starts_with("---") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let rest = trimmed[3..].trim_start_matches(['\r', '\n']);
|
||||||
|
let end_idx = rest.find("\n---")?;
|
||||||
|
let yaml = &rest[..end_idx];
|
||||||
|
let value: serde_yaml::Value = serde_yaml::from_str(yaml).ok()?;
|
||||||
|
value
|
||||||
|
.get("name")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.map(|s| s.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn install_from_local(source: &str, target_dir: &Path) -> Result<String, String> {
|
async fn install_from_local(source: &str, target_dir: &Path) -> Result<String, String> {
|
||||||
@@ -407,12 +585,14 @@ pub async fn search_marketplace(
|
|||||||
source: Option<String>,
|
source: Option<String>,
|
||||||
) -> Result<Vec<MarketplaceSkill>, String> {
|
) -> Result<Vec<MarketplaceSkill>, String> {
|
||||||
let installed_refs = installed_source_refs();
|
let installed_refs = installed_source_refs();
|
||||||
|
let query = query.trim();
|
||||||
|
let builtin_results = builtin_marketplace_results(query, &installed_refs);
|
||||||
|
|
||||||
match source.as_deref().unwrap_or("skills.sh") {
|
match source.as_deref().unwrap_or("skills.sh") {
|
||||||
"github" => {
|
"github" => {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"https://api.github.com/search/repositories?q={}+topic:agent-skill&sort=stars&per_page=20",
|
"https://api.github.com/search/repositories?q={}+topic:agent-skill&sort=stars&per_page=20",
|
||||||
urlencoding::encode(&query)
|
urlencoding::encode(query)
|
||||||
);
|
);
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
@@ -431,7 +611,7 @@ pub async fn search_marketplace(
|
|||||||
let body: serde_json::Value = response.json().await.map_err(|e| e.to_string())?;
|
let body: serde_json::Value = response.json().await.map_err(|e| e.to_string())?;
|
||||||
let items = body["items"].as_array().cloned().unwrap_or_default();
|
let items = body["items"].as_array().cloned().unwrap_or_default();
|
||||||
|
|
||||||
let results: Vec<MarketplaceSkill> = items
|
let mut results: Vec<MarketplaceSkill> = items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|item| {
|
.map(|item| {
|
||||||
let skill_name = item["name"].as_str().unwrap_or("").to_string();
|
let skill_name = item["name"].as_str().unwrap_or("").to_string();
|
||||||
@@ -449,12 +629,13 @@ pub async fn search_marketplace(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
merge_marketplace_results(&mut results, builtin_results);
|
||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"https://skills.sh/api/search?q={}",
|
"https://skills.sh/api/search?q={}",
|
||||||
urlencoding::encode(&query)
|
urlencoding::encode(query)
|
||||||
);
|
);
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
@@ -472,7 +653,7 @@ pub async fn search_marketplace(
|
|||||||
let body: serde_json::Value = response.json().await.map_err(|e| e.to_string())?;
|
let body: serde_json::Value = response.json().await.map_err(|e| e.to_string())?;
|
||||||
let items = body["skills"].as_array().cloned().unwrap_or_default();
|
let items = body["skills"].as_array().cloned().unwrap_or_default();
|
||||||
|
|
||||||
let results: Vec<MarketplaceSkill> = items
|
let mut results: Vec<MarketplaceSkill> = items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|item| {
|
.map(|item| {
|
||||||
let skill_name = item["name"].as_str().unwrap_or("").to_string();
|
let skill_name = item["name"].as_str().unwrap_or("").to_string();
|
||||||
@@ -490,11 +671,69 @@ pub async fn search_marketplace(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
merge_marketplace_results(&mut results, builtin_results);
|
||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn builtin_marketplace_results(
|
||||||
|
query: &str,
|
||||||
|
installed_refs: &std::collections::HashSet<String>,
|
||||||
|
) -> Vec<MarketplaceSkill> {
|
||||||
|
let normalized_query = query.to_lowercase();
|
||||||
|
let tokens: Vec<&str> = normalized_query
|
||||||
|
.split(|ch: char| ch.is_whitespace() || ch == '/' || ch == '-' || ch == '_' || ch == '.')
|
||||||
|
.filter(|token| !token.is_empty())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
BUILTIN_MARKETPLACE_SKILLS
|
||||||
|
.iter()
|
||||||
|
.filter(|skill| {
|
||||||
|
if tokens.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let haystack = format!(
|
||||||
|
"{} {} {} {}",
|
||||||
|
skill.name,
|
||||||
|
skill.description,
|
||||||
|
skill.repo,
|
||||||
|
skill.keywords.join(" ")
|
||||||
|
)
|
||||||
|
.to_lowercase();
|
||||||
|
tokens.iter().all(|token| haystack.contains(token))
|
||||||
|
})
|
||||||
|
.map(|skill| MarketplaceSkill {
|
||||||
|
name: skill.name.to_string(),
|
||||||
|
description: skill.description.to_string(),
|
||||||
|
repo: skill.repo.to_string(),
|
||||||
|
stars: skill.stars,
|
||||||
|
installs: skill.installs,
|
||||||
|
installed: installed_refs.contains(&skill.repo.to_lowercase()),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_marketplace_results(
|
||||||
|
results: &mut Vec<MarketplaceSkill>,
|
||||||
|
builtin_results: Vec<MarketplaceSkill>,
|
||||||
|
) {
|
||||||
|
for builtin in builtin_results.into_iter().rev() {
|
||||||
|
if let Some(existing) = results
|
||||||
|
.iter_mut()
|
||||||
|
.find(|item| item.repo.eq_ignore_ascii_case(&builtin.repo))
|
||||||
|
{
|
||||||
|
if existing.description.is_empty() {
|
||||||
|
existing.description = builtin.description;
|
||||||
|
}
|
||||||
|
existing.name = builtin.name;
|
||||||
|
existing.installed = existing.installed || builtin.installed;
|
||||||
|
} else {
|
||||||
|
results.insert(0, builtin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn check_skill_updates() -> Result<Vec<SkillUpdateInfo>, String> {
|
pub async fn check_skill_updates() -> Result<Vec<SkillUpdateInfo>, String> {
|
||||||
let skills_path = skills_dir();
|
let skills_path = skills_dir();
|
||||||
|
|||||||
@@ -520,6 +520,8 @@ pub fn run() {
|
|||||||
version = %app.package_info().version,
|
version = %app.package_info().version,
|
||||||
"BoosApi setup started"
|
"BoosApi setup started"
|
||||||
);
|
);
|
||||||
|
commands::skills::seed_builtin_skills().expect("failed to seed built-in skills");
|
||||||
|
tracing::info!("BoosApi built-in skills seeded");
|
||||||
|
|
||||||
// Ensure ~/Documents/boosapi/{images,files,backups}/ exist
|
// Ensure ~/Documents/boosapi/{images,files,backups}/ exist
|
||||||
boosapi_core::storage_paths::ensure_documents_dirs()
|
boosapi_core::storage_paths::ensure_documents_dirs()
|
||||||
|
|||||||
@@ -33,6 +33,57 @@ function generateBrowserResponse(userContent: string): string {
|
|||||||
return `收到你的消息:「${userContent.length > 50 ? userContent.slice(0, 50) + '...' : userContent}」\n\n当前为浏览器预览模式,无法调用真实 AI 接口。此模式用于 UI 开发和体验测试。\n\n如需 AI 回复,请使用 \`cargo tauri dev\` 启动完整应用。`;
|
return `收到你的消息:「${userContent.length > 50 ? userContent.slice(0, 50) + '...' : userContent}」\n\n当前为浏览器预览模式,无法调用真实 AI 接口。此模式用于 UI 开发和体验测试。\n\n如需 AI 回复,请使用 \`cargo tauri dev\` 启动完整应用。`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BUILT_IN_SKILLS = [
|
||||||
|
{
|
||||||
|
name: 'jianying-editor',
|
||||||
|
description: '剪映 AI 自动化剪辑 Skill,支持草稿生成、素材导入、字幕、配音、特效和导出。',
|
||||||
|
repo: 'luoluoluo22/jianying-editor-skill',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'seedance',
|
||||||
|
description: '中文 Seedance 2.0 / 即梦视频与图片提示词工程,支持 @素材引用、分镜、运镜和长视频流水线。',
|
||||||
|
repo: 'MapleShaw/seedance2.0-prompt-skill',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'seedance-prompt-en',
|
||||||
|
description: 'English Seedance 2.0 multimodal AI video prompt writing guide.',
|
||||||
|
repo: 'dexhunter/seedance2-skill',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dwg-mcp-server',
|
||||||
|
description: 'DWG MCP Server 包装技能:指导 Agent 安装并使用 dwg-mcp 检查 AutoCAD DWG 文件。',
|
||||||
|
repo: 'dimitrovakulenko/dwg-mcp-server',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cad-ai-agent',
|
||||||
|
description: 'CAD/DXF Agent 包装技能:用于图纸摘要、合规检查、工程量统计、RFI 和安全编辑流程。',
|
||||||
|
repo: 'jeremylongshore/cad-ai-agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'gorden-ppt-skill',
|
||||||
|
description: '中文 PPT 生成与编辑 Skill,支持模板套用、版式保持、出框检测和 PowerPoint 成品导出。',
|
||||||
|
repo: 'GordenSun/GordenPPTSkill',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function builtInSkillRows() {
|
||||||
|
return BUILT_IN_SKILLS.map(skill => ({
|
||||||
|
name: skill.name,
|
||||||
|
description: skill.description,
|
||||||
|
source: 'boosapi',
|
||||||
|
sourcePath: `/mock/skills/${skill.name}/SKILL.md`,
|
||||||
|
enabled: true,
|
||||||
|
hasUpdate: false,
|
||||||
|
userInvocable: true,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeBuiltInSkills(skills: any[]) {
|
||||||
|
const existingNames = new Set(skills.map((skill: any) => skill.name));
|
||||||
|
const missing = builtInSkillRows().filter(skill => !existingNames.has(skill.name));
|
||||||
|
return [...missing, ...skills];
|
||||||
|
}
|
||||||
|
|
||||||
// ── Built-in Providers ──────────────────────────────────────────────────
|
// ── Built-in Providers ──────────────────────────────────────────────────
|
||||||
|
|
||||||
const OPENAI_REASONING_OVERRIDES = {
|
const OPENAI_REASONING_OVERRIDES = {
|
||||||
@@ -1584,7 +1635,7 @@ export async function handleCommand<T>(cmd: string, args?: Record<string, unknow
|
|||||||
|
|
||||||
// ── Skills ────────────────────────────────────────────────────────
|
// ── Skills ────────────────────────────────────────────────────────
|
||||||
case 'list_skills':
|
case 'list_skills':
|
||||||
return getStore<any[]>('skills', []) as T;
|
return mergeBuiltInSkills(getStore<any[]>('skills', [])) as T;
|
||||||
|
|
||||||
case 'get_skill':
|
case 'get_skill':
|
||||||
return {
|
return {
|
||||||
@@ -1613,13 +1664,16 @@ export async function handleCommand<T>(cmd: string, args?: Record<string, unknow
|
|||||||
|
|
||||||
case 'install_skill': {
|
case 'install_skill': {
|
||||||
const source = (args as any)?.source || 'installed-skill';
|
const source = (args as any)?.source || 'installed-skill';
|
||||||
const name = source.includes('jianying-editor-skill') ? 'jianying-editor' : String(source).split('/').pop();
|
const known = BUILT_IN_SKILLS.find(skill => (
|
||||||
|
skill.repo.toLowerCase() === String(source).replace(/^https:\/\/github\.com\//, '').replace(/\.git$/, '').toLowerCase()
|
||||||
|
));
|
||||||
|
const name = known?.name || (source.includes('jianying-editor-skill') ? 'jianying-editor' : String(source).split('/').pop());
|
||||||
const skills = getStore<any[]>('skills', []);
|
const skills = getStore<any[]>('skills', []);
|
||||||
const nextSkill = {
|
const nextSkill = {
|
||||||
name,
|
name,
|
||||||
description: name === 'jianying-editor'
|
description: known?.description || (name === 'jianying-editor'
|
||||||
? '剪映 AI 自动化剪辑 Skill,支持草稿生成、素材导入、字幕、配音、特效和导出。'
|
? '剪映 AI 自动化剪辑 Skill,支持草稿生成、素材导入、字幕、配音、特效和导出。'
|
||||||
: 'Installed skill',
|
: 'Installed skill'),
|
||||||
source: 'boosapi',
|
source: 'boosapi',
|
||||||
sourcePath: `/mock/skills/${name}`,
|
sourcePath: `/mock/skills/${name}`,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -1646,7 +1700,14 @@ export async function handleCommand<T>(cmd: string, args?: Record<string, unknow
|
|||||||
return undefined as T;
|
return undefined as T;
|
||||||
|
|
||||||
case 'search_marketplace':
|
case 'search_marketplace':
|
||||||
return [] as T;
|
return BUILT_IN_SKILLS.map(skill => ({
|
||||||
|
name: skill.name,
|
||||||
|
description: skill.description,
|
||||||
|
repo: skill.repo,
|
||||||
|
stars: 0,
|
||||||
|
installs: 0,
|
||||||
|
installed: mergeBuiltInSkills(getStore<any[]>('skills', [])).some((row: any) => row.name === skill.name),
|
||||||
|
})) as T;
|
||||||
|
|
||||||
case 'check_skill_updates':
|
case 'check_skill_updates':
|
||||||
return [] as T;
|
return [] as T;
|
||||||
|
|||||||
@@ -28,7 +28,56 @@ const openExternalUrl = (url: string) => {
|
|||||||
|
|
||||||
const { Text, Paragraph } = Typography;
|
const { Text, Paragraph } = Typography;
|
||||||
|
|
||||||
|
const BUILTIN_MARKETPLACE_DETAILS: Record<string, string> = {
|
||||||
|
'dimitrovakulenko/dwg-mcp-server': `# DWG MCP Server Skill
|
||||||
|
|
||||||
|
Use this skill when a user wants to inspect AutoCAD DWG drawings with an AI agent.
|
||||||
|
|
||||||
|
Upstream project: https://github.com/dimitrovakulenko/dwg-mcp-server
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
npx -y @dmytro-prototypes/dwg-mcp-server
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
For clients without MCP roots:
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
DWG_MCP_ALLOWED_ROOTS="$HOME" npx -y @dmytro-prototypes/dwg-mcp-server
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Ask for the DWG file path and confirm it is under an allowed root.
|
||||||
|
2. Use \`dwg.list_roots\`, \`dwg.open_file\`, \`dwg.list_file_types\`, \`dwg.describe_type\`, \`dwg.get_objects\`, and \`dwg.query_objects\`.
|
||||||
|
3. Summarize entities, layers, blocks, layouts, references, and notable properties.
|
||||||
|
4. Close the document with \`dwg.close_file\`.
|
||||||
|
`,
|
||||||
|
'jeremylongshore/cad-ai-agent': `# CAD AI Agent Skill
|
||||||
|
|
||||||
|
Use this skill for CAD/DXF drawing summaries, compliance checks, quantity takeoffs, RFIs, health reports, revision comparison, and safe edit planning.
|
||||||
|
|
||||||
|
Upstream project: https://github.com/jeremylongshore/cad-ai-agent
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
git clone https://github.com/jeremylongshore/cad-ai-agent.git
|
||||||
|
cd cad-ai-agent
|
||||||
|
python -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install -e ".[dev]"
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Identify drawing format and user intent.
|
||||||
|
2. Preserve originals and prefer save-as outputs.
|
||||||
|
3. For edits, produce structured operation plans rather than touching raw CAD data directly.
|
||||||
|
4. For analysis, return layer/entity summaries, issues, measurements, and concrete next actions.
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
const SOURCE_ICONS: Record<string, React.ReactNode> = {
|
const SOURCE_ICONS: Record<string, React.ReactNode> = {
|
||||||
boosapi: <img src={appLogo} alt="" style={{ width: 14, height: 14, verticalAlign: 'middle' }} />,
|
boosapi: <img src={appLogo} alt="" style={{ width: 14, height: 14, verticalAlign: 'middle' }} />,
|
||||||
@@ -299,10 +348,10 @@ export function SkillsPage() {
|
|||||||
setMarketplaceDetailContent({ name: skill.name, repo: skill.repo, content: '' });
|
setMarketplaceDetailContent({ name: skill.name, repo: skill.repo, content: '' });
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`https://raw.githubusercontent.com/${repo}/main/SKILL.md`);
|
const res = await fetch(`https://raw.githubusercontent.com/${repo}/main/SKILL.md`);
|
||||||
const content = res.ok ? await res.text() : '(SKILL.md not found)';
|
const content = res.ok ? await res.text() : (BUILTIN_MARKETPLACE_DETAILS[repo] || '(SKILL.md not found)');
|
||||||
setMarketplaceDetailContent({ name: skill.name, repo: skill.repo, content });
|
setMarketplaceDetailContent({ name: skill.name, repo: skill.repo, content });
|
||||||
} catch {
|
} catch {
|
||||||
setMarketplaceDetailContent({ name: skill.name, repo: skill.repo, content: '(Failed to fetch SKILL.md)' });
|
setMarketplaceDetailContent({ name: skill.name, repo: skill.repo, content: BUILTIN_MARKETPLACE_DETAILS[repo] || '(Failed to fetch SKILL.md)' });
|
||||||
} finally {
|
} finally {
|
||||||
setMarketplaceDetailLoading(false);
|
setMarketplaceDetailLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
export const SITE_DOCS_URL = 'https://boosapi.cita777.me';
|
export const SITE_DOCS_URL = 'https://tybossai.com';
|
||||||
export const SITE_GITHUB_URL = 'https://github.com/cita-777';
|
export const SITE_GITHUB_URL = 'https://github.com/Boos4721';
|
||||||
|
|||||||
+80
-1
@@ -220,6 +220,7 @@ a {
|
|||||||
.studio-section,
|
.studio-section,
|
||||||
.pipeline-section,
|
.pipeline-section,
|
||||||
.control-section,
|
.control-section,
|
||||||
|
.opensource-section,
|
||||||
.deploy-section,
|
.deploy-section,
|
||||||
.final-section {
|
.final-section {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -972,6 +973,7 @@ a {
|
|||||||
.studio-section,
|
.studio-section,
|
||||||
.pipeline-section,
|
.pipeline-section,
|
||||||
.control-section,
|
.control-section,
|
||||||
|
.opensource-section,
|
||||||
.deploy-section,
|
.deploy-section,
|
||||||
.final-section {
|
.final-section {
|
||||||
padding-block: 72px;
|
padding-block: 72px;
|
||||||
@@ -994,6 +996,7 @@ a {
|
|||||||
.studio-copy span,
|
.studio-copy span,
|
||||||
.pipeline-copy span,
|
.pipeline-copy span,
|
||||||
.control-panel span,
|
.control-panel span,
|
||||||
|
.opensource-copy span,
|
||||||
.ops-copy span,
|
.ops-copy span,
|
||||||
.console-preview-copy > span,
|
.console-preview-copy > span,
|
||||||
.factory-copy span,
|
.factory-copy span,
|
||||||
@@ -1011,6 +1014,7 @@ a {
|
|||||||
.studio-copy h2,
|
.studio-copy h2,
|
||||||
.pipeline-copy h2,
|
.pipeline-copy h2,
|
||||||
.control-panel h2,
|
.control-panel h2,
|
||||||
|
.opensource-copy h2,
|
||||||
.ops-copy h2,
|
.ops-copy h2,
|
||||||
.console-preview-copy h2,
|
.console-preview-copy h2,
|
||||||
.factory-copy h2,
|
.factory-copy h2,
|
||||||
@@ -1141,6 +1145,7 @@ a {
|
|||||||
.pipeline-steps article,
|
.pipeline-steps article,
|
||||||
.pipeline-output,
|
.pipeline-output,
|
||||||
.control-grid > div,
|
.control-grid > div,
|
||||||
|
.github-card,
|
||||||
.ops-card,
|
.ops-card,
|
||||||
.console-preview-stats > div,
|
.console-preview-stats > div,
|
||||||
.preview-window,
|
.preview-window,
|
||||||
@@ -1156,6 +1161,7 @@ a {
|
|||||||
.pipeline-steps article:hover,
|
.pipeline-steps article:hover,
|
||||||
.pipeline-output:hover,
|
.pipeline-output:hover,
|
||||||
.control-grid > div:hover,
|
.control-grid > div:hover,
|
||||||
|
.github-card:hover,
|
||||||
.ops-card:hover,
|
.ops-card:hover,
|
||||||
.console-preview-stats > div:hover,
|
.console-preview-stats > div:hover,
|
||||||
.preview-window:hover,
|
.preview-window:hover,
|
||||||
@@ -1818,6 +1824,71 @@ a {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.opensource-section {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
|
||||||
|
gap: clamp(28px, 4vw, 56px);
|
||||||
|
align-items: stretch;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 32px;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 16% 12%, color-mix(in srgb, var(--acid) 18%, transparent), transparent 34%),
|
||||||
|
linear-gradient(135deg, var(--panel-strong), color-mix(in srgb, var(--surface) 86%, transparent));
|
||||||
|
padding: clamp(36px, 5vw, 62px);
|
||||||
|
box-shadow: inset 0 1px 0 var(--surface-highlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.opensource-copy {
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opensource-copy p,
|
||||||
|
.github-card p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 1.72;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opensource-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-card {
|
||||||
|
min-height: 142px;
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 22px;
|
||||||
|
background: var(--surface);
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: inset 0 1px 0 var(--surface-highlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-card span {
|
||||||
|
color: var(--faint);
|
||||||
|
font-family: var(--font-geist-mono), monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 760;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-card strong {
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 22px;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
.deploy-section {
|
.deploy-section {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 0.92fr) minmax(min(280px, 100%), 0.72fr);
|
grid-template-columns: minmax(0, 0.92fr) minmax(min(280px, 100%), 0.72fr);
|
||||||
@@ -1923,6 +1994,7 @@ a {
|
|||||||
.studio-section,
|
.studio-section,
|
||||||
.control-section,
|
.control-section,
|
||||||
.factory-section,
|
.factory-section,
|
||||||
|
.opensource-section,
|
||||||
.deploy-section {
|
.deploy-section {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
@@ -1954,6 +2026,7 @@ a {
|
|||||||
.console-preview-section,
|
.console-preview-section,
|
||||||
.factory-section,
|
.factory-section,
|
||||||
.control-section,
|
.control-section,
|
||||||
|
.opensource-section,
|
||||||
.deploy-section {
|
.deploy-section {
|
||||||
gap: 28px;
|
gap: 28px;
|
||||||
margin-block: 24px;
|
margin-block: 24px;
|
||||||
@@ -1973,6 +2046,7 @@ a {
|
|||||||
.studio-section,
|
.studio-section,
|
||||||
.control-section,
|
.control-section,
|
||||||
.factory-section,
|
.factory-section,
|
||||||
|
.opensource-section,
|
||||||
.deploy-section,
|
.deploy-section,
|
||||||
.final-section {
|
.final-section {
|
||||||
width: min(100% - 28px, 1180px);
|
width: min(100% - 28px, 1180px);
|
||||||
@@ -2060,6 +2134,7 @@ a {
|
|||||||
.pipeline-copy,
|
.pipeline-copy,
|
||||||
.pipeline-board,
|
.pipeline-board,
|
||||||
.control-section,
|
.control-section,
|
||||||
|
.opensource-section,
|
||||||
.ops-copy,
|
.ops-copy,
|
||||||
.ops-grid,
|
.ops-grid,
|
||||||
.console-preview-copy,
|
.console-preview-copy,
|
||||||
@@ -2108,6 +2183,7 @@ a {
|
|||||||
.control-section,
|
.control-section,
|
||||||
.pipeline-section,
|
.pipeline-section,
|
||||||
.console-preview-section,
|
.console-preview-section,
|
||||||
|
.opensource-section,
|
||||||
.deploy-section {
|
.deploy-section {
|
||||||
gap: 22px;
|
gap: 22px;
|
||||||
margin-block: 18px;
|
margin-block: 18px;
|
||||||
@@ -2263,6 +2339,7 @@ a {
|
|||||||
.studio-section,
|
.studio-section,
|
||||||
.pipeline-section,
|
.pipeline-section,
|
||||||
.control-section,
|
.control-section,
|
||||||
|
.opensource-section,
|
||||||
.deploy-section,
|
.deploy-section,
|
||||||
.final-section {
|
.final-section {
|
||||||
padding-block: clamp(58px, 7vw, 84px);
|
padding-block: clamp(58px, 7vw, 84px);
|
||||||
@@ -2275,6 +2352,7 @@ a {
|
|||||||
.pipeline-steps article,
|
.pipeline-steps article,
|
||||||
.pipeline-output,
|
.pipeline-output,
|
||||||
.control-grid > div,
|
.control-grid > div,
|
||||||
|
.github-card,
|
||||||
.ops-card,
|
.ops-card,
|
||||||
.console-preview-stats > div,
|
.console-preview-stats > div,
|
||||||
.preview-window,
|
.preview-window,
|
||||||
@@ -2289,6 +2367,7 @@ a {
|
|||||||
.pipeline-steps article:hover,
|
.pipeline-steps article:hover,
|
||||||
.pipeline-output:hover,
|
.pipeline-output:hover,
|
||||||
.control-grid > div:hover,
|
.control-grid > div:hover,
|
||||||
|
.github-card:hover,
|
||||||
.ops-card:hover,
|
.ops-card:hover,
|
||||||
.console-preview-stats > div:hover,
|
.console-preview-stats > div:hover,
|
||||||
.preview-window:hover,
|
.preview-window:hover,
|
||||||
@@ -2304,6 +2383,7 @@ a {
|
|||||||
.console-preview-board,
|
.console-preview-board,
|
||||||
.factory-section,
|
.factory-section,
|
||||||
.control-section,
|
.control-section,
|
||||||
|
.opensource-section,
|
||||||
.deploy-section,
|
.deploy-section,
|
||||||
.final-section {
|
.final-section {
|
||||||
border-color: color-mix(in srgb, var(--line) 82%, transparent);
|
border-color: color-mix(in srgb, var(--line) 82%, transparent);
|
||||||
@@ -3073,4 +3153,3 @@ a {
|
|||||||
will-change: auto;
|
will-change: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -202,6 +202,27 @@ const productionModules = [
|
|||||||
"PostgreSQL",
|
"PostgreSQL",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const githubProjects = [
|
||||||
|
{
|
||||||
|
name: "Boos4721",
|
||||||
|
href: "https://github.com/Boos4721",
|
||||||
|
body: "BossAI/BoosAPI 的公开工程入口,桌面端、网关、工作流和 Provider 相关更新都会从这里沉淀。",
|
||||||
|
meta: "GitHub profile",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "volc-visual-sdk-rs",
|
||||||
|
href: "https://github.com/Boos4721/volc-visual-sdk-rs",
|
||||||
|
body: "面向火山智能视觉的 Rust 签名能力,支撑即梦图片与视频 Provider 的工程化接入。",
|
||||||
|
meta: "Rust · Visual API",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ccwatch",
|
||||||
|
href: "https://github.com/Boos4721/ccwatch",
|
||||||
|
body: "围绕 Claude Code / Codex 的本地开发辅助工具,和 BossAI Desktop 的 Agent 工作台方向一致。",
|
||||||
|
meta: "Agent tooling",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const controlSignals = [
|
const controlSignals = [
|
||||||
"模型路由",
|
"模型路由",
|
||||||
"Claude Messages",
|
"Claude Messages",
|
||||||
@@ -218,6 +239,7 @@ const navItems = [
|
|||||||
["workflow", "工作流"],
|
["workflow", "工作流"],
|
||||||
["operations", "运营"],
|
["operations", "运营"],
|
||||||
["factory", "创作"],
|
["factory", "创作"],
|
||||||
|
["opensource", "开源"],
|
||||||
["deploy", "部署"],
|
["deploy", "部署"],
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
@@ -350,6 +372,9 @@ export default function Home() {
|
|||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
<div className="nav-actions">
|
<div className="nav-actions">
|
||||||
|
<a className="ghost-link" href="https://github.com/Boos4721" target="_blank" rel="noopener noreferrer">
|
||||||
|
GitHub
|
||||||
|
</a>
|
||||||
<Link className="ghost-link" href="/user/login">
|
<Link className="ghost-link" href="/user/login">
|
||||||
用户登录
|
用户登录
|
||||||
</Link>
|
</Link>
|
||||||
@@ -385,6 +410,9 @@ export default function Home() {
|
|||||||
<Link className="secondary-cta" href="#workflow">
|
<Link className="secondary-cta" href="#workflow">
|
||||||
查看工作流
|
查看工作流
|
||||||
</Link>
|
</Link>
|
||||||
|
<a className="secondary-cta" href="https://github.com/Boos4721" target="_blank" rel="noopener noreferrer">
|
||||||
|
GitHub 开源
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="hero-proof" aria-label="BossAI 兼容能力">
|
<div className="hero-proof" aria-label="BossAI 兼容能力">
|
||||||
{heroStats.map((stat) => (
|
{heroStats.map((stat) => (
|
||||||
@@ -668,6 +696,33 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="opensource" className="opensource-section" data-motion="section">
|
||||||
|
<div className="opensource-copy">
|
||||||
|
<span>Open Source</span>
|
||||||
|
<h2>官网入口、桌面端和 Provider 工程都可以从 GitHub 追踪</h2>
|
||||||
|
<p>
|
||||||
|
BossAI 不是孤立的展示页。Boos4721 的 GitHub 主页汇总了 Rust Provider、Agent 工具和桌面端相关工程,方便部署者查看更新、复用组件和反馈问题。
|
||||||
|
</p>
|
||||||
|
<div className="opensource-actions">
|
||||||
|
<a className="primary-cta" href="https://github.com/Boos4721" target="_blank" rel="noopener noreferrer">
|
||||||
|
访问 Boos4721
|
||||||
|
</a>
|
||||||
|
<a className="secondary-cta" href="https://github.com/Boos4721/volc-visual-sdk-rs" target="_blank" rel="noopener noreferrer">
|
||||||
|
查看即梦 SDK
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="github-grid" aria-label="Boos4721 GitHub 项目">
|
||||||
|
{githubProjects.map((project) => (
|
||||||
|
<a className="github-card" data-motion="card" href={project.href} key={project.name} target="_blank" rel="noopener noreferrer">
|
||||||
|
<span>{project.meta}</span>
|
||||||
|
<strong>{project.name}</strong>
|
||||||
|
<p>{project.body}</p>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section id="deploy" className="deploy-section" data-motion="section">
|
<section id="deploy" className="deploy-section" data-motion="section">
|
||||||
<div>
|
<div>
|
||||||
<span>Deploy</span>
|
<span>Deploy</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user