# 创建第一个插件

> 创建、构建并安装一个最小 Astravia 桌面插件。

Canonical page: /plugins/getting-started



## 前置条件 [#前置条件]

* Node 或 Bun。
* 用于安装和调试插件的 Astravia 桌面客户端（安装与热更新都要求它正在运行）。

**不需要** Astravia 的源码仓库。

## 一条命令起工程 [#一条命令起工程]

```bash
npx @astravia-org/plugin-cli init --id my-plugin --name "My Plugin"
cd my-plugin && npm install
```

脚手架会落下 `plugin.json`、Vite + Module Federation 配置、Tailwind 入口，以及一份 `AGENTS.md`。

<Callout title="给 Agent 的第一步" type="info">
  开发手册随 `@astravia-org/plugin-sdk` 装进 `node_modules`，**不要硬编码那个路径**——工作区可能
  把依赖提升到仓库根，一仓多插件时各插件还可能钉不同的 SDK 版本。用命令解析：

  ```bash
  npx astravia-plugin-cli docs
  ```

  它打印手册目录的绝对路径与对应的 SDK 版本。因为手册与 SDK 同版本发布，Agent 读到的合同
  **就是这个工程即将编译的合同**——从网络现取最新文档做不到这一点。
</Callout>

## 开发闭环 [#开发闭环]

```bash
npm run dev            # Vite + Module Federation 开发服务器
npm run validate       # 校验清单与构建产物
npm run install:astravia  # 构建、打包并装进正在运行的 Astravia
npx astravia-plugin-cli watch      # 热更新：宿主改从本工程目录加载，改完即生效
npx astravia-plugin-cli reload <id>  # 提示有 pending 版本时切换过去
npx astravia-plugin-cli uninstall  # 卸载
```

`install:astravia` 把归档交给正在运行的桌面端校验、授权、安装，**不会**直接写 `~/.astravia/plugins`。

安装一个更新版本只被记录为 pending，应用继续加载当前活动版本，直到 `reload` 才切换。改了代码却看不到变化时，先确认 `plugin.json` 的 `version` 已经提升。

## 项目结构 [#项目结构]

<Files>
  <Folder name="my-plugin">
    <File name="plugin.json" />

    <File name="package.json" />

    <File name="tsconfig.json" />

    <File name="vite.config.ts" />

    <Folder name="src">
      <File name="index.tsx" />

      <File name="style.css" />
    </Folder>
  </Folder>
</Files>

## 配置构建 [#配置构建]

通过 `@astravia-org/plugin-vite` 配置 Module Federation：

```ts
import tailwindcss from "@tailwindcss/vite";
import { astraviaPluginFederation } from "@astravia-org/plugin-vite";
import { defineConfig } from "vite";

export default defineConfig({
	plugins: [
		tailwindcss(),
		astraviaPluginFederation({
			name: "my_plugin",
			entry: "./src/index.tsx",
			expose: "./plugin",
		}),
	],
	esbuild: { jsx: "automatic", jsxImportSource: "react" },
});
```

## 创建入口 [#创建入口]

```tsx
import { definePlugin } from "@astravia-org/plugin-sdk";

function MyPanel() {
	return <div className="p-3 text-sm text-foreground">插件已加载</div>;
}

export default definePlugin({
	activate(ctx) {
		ctx.ui.registerGlobalSlot({ id: "root", component: MyPanel });
	},
});
```

<Callout title="不要在模块顶层创建 JSX" type="warn">
  Module Federation 的共享 React 依赖在 bootstrap 后才可用。把 JSX 放在组件或 `activate()` 调用后的执行路径中。
</Callout>

## 构建与加载 [#构建与加载]

按开发调试与正式发布选择路径：

<Tabs items="[&#x22;开发调试&#x22;, &#x22;发布安装&#x22;]">
  <Tab value="开发调试">
    使用 `bunx astravia-plugin dev` 启动插件开发服务，再通过 Astravia 的开发链接加载。改动后可快速验证界面与工具注册，但发布前仍要执行生产构建并测试 ZIP 安装流程。
  </Tab>

  <Tab value="发布安装">
    <Steps>
      <Step>
        ### 生产构建 [#生产构建]

        运行 `bunx vite build`，生成 `dist/mf-manifest.json`、`remoteEntry.js` 和样式文件。
      </Step>

      <Step>
        ### 打包归档 [#打包归档]

        发布包使用 `.astraviapkg` 扩展名和 ZIP 容器，并在归档根目录包含 `plugin.json` 和 `dist/`。安装了 Astravia 后可以双击该文件安装。
      </Step>

      <Step>
        ### 安装并启用 [#安装并启用]

        在侧栏 **能力** 页打开 **添加能力 → 导入插件压缩包**，选择 ZIP；安装后在 **启用与权限** 对话框中启用并授权。
      </Step>

      <Step>
        ### 验证加载 [#验证加载]

        打开插件注册的界面或触发其工具，确认产物确实被宿主加载。
      </Step>
    </Steps>
  </Tab>
</Tabs>

## 发布多个能力：能力市场仓库 [#发布多个能力能力市场仓库]

一个仓库要上架多个能力（插件、MCP、Skill、场景）时，用市场骨架：

```bash
npx @astravia-org/plugin-cli init hub --name my-market \
  --repository https://github.com/me/my-market --min-app-version 0.55.0
```

它生成 `.astravia/marketplace.json` 索引、`abilities/` 目录约定、一份仓库级 `AGENTS.md`，以及跑
`sync --check` 的 CI。之后在 `abilities/plugins/<slug>` 里照常 `init` 即可——**开发命令一律作用于
最近的那个能力目录**，所以在市场仓库里开发与开发单插件完全一样。

索引里的 `version`、`config.api_version`、`config.permissions` 都是从能力包推导的派生字段，
由工具对账：

```bash
npx @astravia-org/plugin-cli sync          # 回填派生字段，并推进 marketplaceVersion
npx @astravia-org/plugin-cli sync --check  # 只报不写，非零退出（CI 用）
```

<Callout title="三条会咬人的约束" type="warn">
  索引条目的 `version` 与能力包不等时，宿主同步**直接失败**；`plugin.json` 的 `entry` / `styles`
  不在已发布目录里时，本地能装、市场上装不了（客户端不会替你构建，所以那些目录要提交 `dist/`）；
  改了内容却没换 `marketplaceVersion` 时，客户端既不报错也不更新——用户只是永远收不到。
  `sync --check` 三条都会守住。
</Callout>

## 下一步 [#下一步]

<Cards>
  <Card title="清单与权限" href="/plugins/manifest-and-permissions/" description="对齐 plugin.json 字段、权限声明与发布前检查。" />

  <Card title="扩展点" href="/plugins/extension-points/" description="选择 UI、文件、消息、Agent 或 App Action 扩展点。" />
</Cards>
