diff --git a/docs/channels/line.md b/docs/channels/line.md index 40ed2f9f6..43c840b43 100644 --- a/docs/channels/line.md +++ b/docs/channels/line.md @@ -16,20 +16,27 @@ Status: supported via plugin. Direct messages, group chats, media, locations, Fl messages, template messages, and quick replies are supported. Reactions and threads are not supported. -## Plugin required +## Plugin setup -Install the LINE plugin: +LINE is bundled with Clawdbot. Its dependencies are automatically installed on first load +when you enable the plugin. + +To enable, add `line` to your plugins allow list: + +```json5 +{ + plugins: { + allow: ["line"] + } +} +``` + +Alternatively, install explicitly from npm: ```bash clawdbot plugins install @clawdbot/line ``` -Local checkout (when running from a git repo): - -```bash -clawdbot plugins install ./extensions/line -``` - ## Setup 1) Create a LINE Developers account and open the Console: diff --git a/docs/channels/matrix.md b/docs/channels/matrix.md index 2d9025f51..3073be6d2 100644 --- a/docs/channels/matrix.md +++ b/docs/channels/matrix.md @@ -13,32 +13,32 @@ but it requires E2EE to be enabled. Status: supported via plugin (matrix-bot-sdk). Direct messages, rooms, threads, media, reactions, polls (send + poll-start as text), location, and E2EE (with crypto support). -## Plugin required +## Plugin setup -Matrix ships as a plugin and is not bundled with the core install. +Matrix is bundled with Clawdbot. Its dependencies are automatically installed on first load +when you enable the plugin. -Install via CLI (npm registry): +To enable, add `matrix` to your plugins allow list: + +```json5 +{ + plugins: { + allow: ["matrix"] + } +} +``` + +Alternatively, install explicitly from npm: ```bash clawdbot plugins install @clawdbot/matrix ``` -Local checkout (when running from a git repo): - -```bash -clawdbot plugins install ./extensions/matrix -``` - -If you choose Matrix during configure/onboarding and a git checkout is detected, -Clawdbot will offer the local install path automatically. - Details: [Plugins](/plugin) ## Setup -1) Install the Matrix plugin: - - From npm: `clawdbot plugins install @clawdbot/matrix` - - From a local checkout: `clawdbot plugins install ./extensions/matrix` +1) Add `matrix` to your plugins allow list (or install via CLI). 2) Create a Matrix account on a homeserver: - Browse hosting options at [https://matrix.org/ecosystem/hosting/](https://matrix.org/ecosystem/hosting/) - Or host it yourself. diff --git a/docs/channels/mattermost.md b/docs/channels/mattermost.md index de4771745..9cc10ece9 100644 --- a/docs/channels/mattermost.md +++ b/docs/channels/mattermost.md @@ -11,22 +11,29 @@ Status: supported via plugin (bot token + WebSocket events). Channels, groups, a Mattermost is a self-hostable team messaging platform; see the official site at [mattermost.com](https://mattermost.com) for product details and downloads. -## Plugin required -Mattermost ships as a plugin and is not bundled with the core install. +## Plugin setup + +Mattermost is bundled with Clawdbot. Its dependencies are automatically installed on first +load when you enable the plugin. + +To enable, add `mattermost` to your plugins allow list: + +```json5 +{ + plugins: { + allow: ["mattermost"] + } +} +``` + +The onboarding wizard and `clawdbot channels add` also offer to enable Mattermost. + +Alternatively, install explicitly from npm: -Install via CLI (npm registry): ```bash clawdbot plugins install @clawdbot/mattermost ``` -Local checkout (when running from a git repo): -```bash -clawdbot plugins install ./extensions/mattermost -``` - -If you choose Mattermost during configure/onboarding and a git checkout is detected, -Clawdbot will offer the local install path automatically. - Details: [Plugins](/plugin) ## Quick setup diff --git a/docs/channels/msteams.md b/docs/channels/msteams.md index 2f6ed5f83..382aef5f3 100644 --- a/docs/channels/msteams.md +++ b/docs/channels/msteams.md @@ -12,30 +12,34 @@ Updated: 2026-01-21 Status: text + DM attachments are supported; channel/group file sending requires `sharePointSiteId` + Graph permissions (see [Sending files in group chats](#sending-files-in-group-chats)). Polls are sent via Adaptive Cards. -## Plugin required -Microsoft Teams ships as a plugin and is not bundled with the core install. +## Plugin setup -**Breaking change (2026.1.15):** MS Teams moved out of core. If you use it, you must install the plugin. +Microsoft Teams is bundled with Clawdbot. Its dependencies are automatically installed on +first load when you enable the plugin. -Explainable: keeps core installs lighter and lets MS Teams dependencies update independently. +**Note (2026.1.15):** MS Teams is a plugin to keep core installs lighter and let its +dependencies update independently. + +To enable, add `msteams` to your plugins allow list: + +```json5 +{ + plugins: { + allow: ["msteams"] + } +} +``` + +Alternatively, install explicitly from npm: -Install via CLI (npm registry): ```bash clawdbot plugins install @clawdbot/msteams ``` -Local checkout (when running from a git repo): -```bash -clawdbot plugins install ./extensions/msteams -``` - -If you choose Teams during configure/onboarding and a git checkout is detected, -Clawdbot will offer the local install path automatically. - Details: [Plugins](/plugin) ## Quick setup (beginner) -1) Install the Microsoft Teams plugin. +1) Add `msteams` to your plugins allow list (or install via CLI). 2) Create an **Azure Bot** (App ID + client secret + tenant ID). 3) Configure Clawdbot with those credentials. 4) Expose `/api/messages` (port 3978 by default) via a public URL or tunnel. diff --git a/docs/channels/nextcloud-talk.md b/docs/channels/nextcloud-talk.md index abc696444..599812a24 100644 --- a/docs/channels/nextcloud-talk.md +++ b/docs/channels/nextcloud-talk.md @@ -7,22 +7,29 @@ read_when: Status: supported via plugin (webhook bot). Direct messages, rooms, reactions, and markdown messages are supported. -## Plugin required -Nextcloud Talk ships as a plugin and is not bundled with the core install. +## Plugin setup + +Nextcloud Talk is bundled with Clawdbot. Its dependencies are automatically installed on first +load when you enable the plugin. + +To enable, add `nextcloud-talk` to your plugins allow list: + +```json5 +{ + plugins: { + allow: ["nextcloud-talk"] + } +} +``` + +The onboarding wizard and `clawdbot channels add` also offer to enable Nextcloud Talk. + +Alternatively, install explicitly from npm: -Install via CLI (npm registry): ```bash clawdbot plugins install @clawdbot/nextcloud-talk ``` -Local checkout (when running from a git repo): -```bash -clawdbot plugins install ./extensions/nextcloud-talk -``` - -If you choose Nextcloud Talk during configure/onboarding and a git checkout is detected, -Clawdbot will offer the local install path automatically. - Details: [Plugins](/plugin) ## Quick setup (beginner) diff --git a/docs/channels/nostr.md b/docs/channels/nostr.md index d1dc284bb..2f1dfc418 100644 --- a/docs/channels/nostr.md +++ b/docs/channels/nostr.md @@ -10,33 +10,31 @@ read_when: Nostr is a decentralized protocol for social networking. This channel enables Clawdbot to receive and respond to encrypted direct messages (DMs) via NIP-04. -## Install (on demand) +## Plugin setup -### Onboarding (recommended) +Nostr is bundled with Clawdbot. Its dependencies are automatically installed on first load +when you enable the plugin. -- The onboarding wizard (`clawdbot onboard`) and `clawdbot channels add` list optional channel plugins. -- Selecting Nostr prompts you to install the plugin on demand. +To enable, add `nostr` to your plugins allow list: -Install defaults: +```json5 +{ + plugins: { + allow: ["nostr"] + } +} +``` -- **Dev channel + git checkout available:** uses the local plugin path. -- **Stable/Beta:** downloads from npm. +The onboarding wizard (`clawdbot onboard`) and `clawdbot channels add` also offer to enable +Nostr when you select it. -You can always override the choice in the prompt. - -### Manual install +Alternatively, install explicitly from npm: ```bash clawdbot plugins install @clawdbot/nostr ``` -Use a local checkout (dev workflows): - -```bash -clawdbot plugins install --link /extensions/nostr -``` - -Restart the Gateway after installing or enabling plugins. +Restart the Gateway after enabling plugins. ## Quick setup diff --git a/docs/channels/tlon.md b/docs/channels/tlon.md index a2436d5e7..2ad6f3f8c 100644 --- a/docs/channels/tlon.md +++ b/docs/channels/tlon.md @@ -12,30 +12,35 @@ be further restricted via allowlists. Status: supported via plugin. DMs, group mentions, thread replies, and text-only media fallback (URL appended to caption). Reactions, polls, and native media uploads are not supported. -## Plugin required +## Plugin setup -Tlon ships as a plugin and is not bundled with the core install. +Tlon is bundled with Clawdbot. Its dependencies (`@urbit/http-api`, `@urbit/aura`) are +automatically installed on first load when you enable the plugin. -Install via CLI (npm registry): +To enable, add `tlon` to your plugins allow list and configure the channel: -```bash -clawdbot plugins install @clawdbot/tlon -``` - -Local checkout (when running from a git repo): - -```bash -clawdbot plugins install ./extensions/tlon +```json5 +{ + plugins: { + allow: ["tlon"] + }, + channels: { + tlon: { + enabled: true, + // ... config below + } + } +} ``` Details: [Plugins](/plugin) ## Setup -1) Install the Tlon plugin. -2) Gather your ship URL and login code. +1) Gather your ship URL and login code. +2) Add `tlon` to your plugins allow list. 3) Configure `channels.tlon`. -4) Restart the gateway. +4) Restart the gateway (dependencies install automatically on first load). 5) DM the bot or mention it in a group channel. Minimal config (single account): diff --git a/docs/channels/zalo.md b/docs/channels/zalo.md index 55f2d5104..87c58be17 100644 --- a/docs/channels/zalo.md +++ b/docs/channels/zalo.md @@ -7,17 +7,31 @@ read_when: Status: experimental. Direct messages only; groups coming soon per Zalo docs. -## Plugin required -Zalo ships as a plugin and is not bundled with the core install. -- Install via CLI: `clawdbot plugins install @clawdbot/zalo` -- Or select **Zalo** during onboarding and confirm the install prompt -- Details: [Plugins](/plugin) +## Plugin setup + +Zalo is bundled with Clawdbot. Its dependencies are automatically installed on first load +when you enable the plugin. + +To enable, add `zalo` to your plugins allow list: + +```json5 +{ + plugins: { + allow: ["zalo"] + } +} +``` + +Or select **Zalo** during onboarding. Alternatively, install explicitly from npm: + +```bash +clawdbot plugins install @clawdbot/zalo +``` + +Details: [Plugins](/plugin) ## Quick setup (beginner) -1) Install the Zalo plugin: - - From a source checkout: `clawdbot plugins install ./extensions/zalo` - - From npm (if published): `clawdbot plugins install @clawdbot/zalo` - - Or pick **Zalo** in onboarding and confirm the install prompt +1) Add `zalo` to your plugins allow list (or install via CLI/onboarding). 2) Set the token: - Env: `ZALO_BOT_TOKEN=...` - Or config: `channels.zalo.botToken: "..."`. diff --git a/docs/channels/zalouser.md b/docs/channels/zalouser.md index 004a9d223..9ba4489d0 100644 --- a/docs/channels/zalouser.md +++ b/docs/channels/zalouser.md @@ -10,11 +10,24 @@ Status: experimental. This integration automates a **personal Zalo account** via > **Warning:** This is an unofficial integration and may result in account suspension/ban. Use at your own risk. -## Plugin required -Zalo Personal ships as a plugin and is not bundled with the core install. -- Install via CLI: `clawdbot plugins install @clawdbot/zalouser` -- Or from a source checkout: `clawdbot plugins install ./extensions/zalouser` -- Details: [Plugins](/plugin) +## Plugin setup + +Zalo Personal is bundled with Clawdbot. Its dependencies are automatically installed on first +load when you enable the plugin. + +To enable, add `zalouser` to your plugins allow list: + +```json5 +{ + plugins: { + allow: ["zalouser"] + } +} +``` + +Alternatively, install explicitly from npm: `clawdbot plugins install @clawdbot/zalouser` + +Details: [Plugins](/plugin) ## Prerequisite: zca-cli The Gateway machine must have the `zca` binary available in `PATH`. diff --git a/docs/plugin.md b/docs/plugin.md index c57a024f2..6dc7a34e1 100644 --- a/docs/plugin.md +++ b/docs/plugin.md @@ -44,6 +44,7 @@ See [Voice Call](/plugins/voice-call) for a concrete example plugin. - [Nostr](/channels/nostr) — `@clawdbot/nostr` - [Zalo](/channels/zalo) — `@clawdbot/zalo` - [Microsoft Teams](/channels/msteams) — `@clawdbot/msteams` +- [Tlon/Urbit](/channels/tlon) — bundled as `tlon` (disabled by default; auto-installs deps) - Google Antigravity OAuth (provider auth) — bundled as `google-antigravity-auth` (disabled by default) - Gemini CLI OAuth (provider auth) — bundled as `google-gemini-cli-auth` (disabled by default) - Qwen OAuth (provider auth) — bundled as `qwen-portal-auth` (disabled by default) @@ -128,8 +129,9 @@ A plugin directory may include a `package.json` with `clawdbot.extensions`: Each entry becomes a plugin. If the pack lists multiple extensions, the plugin id becomes `name/`. -If your plugin imports npm deps, install them in that directory so -`node_modules` is available (`npm install` / `pnpm install`). +If your plugin imports npm deps, bundled plugins will **auto-install** them on +first load. For workspace/linked plugins during development, run `npm install` +or `pnpm install` in the plugin directory manually. ### Channel catalog metadata diff --git a/docs/plugins/voice-call.md b/docs/plugins/voice-call.md index 46713c939..b5e52eb12 100644 --- a/docs/plugins/voice-call.md +++ b/docs/plugins/voice-call.md @@ -28,9 +28,22 @@ The Voice Call plugin runs **inside the Gateway process**. If you use a remote Gateway, install/configure the plugin on the **machine running the Gateway**, then restart the Gateway to load it. -## Install +## Plugin setup -### Option A: install from npm (recommended) +Voice Call is bundled with Clawdbot. Its dependencies are automatically installed on first +load when you enable the plugin. + +To enable, add `voice-call` to your plugins allow list: + +```json5 +{ + plugins: { + allow: ["voice-call"] + } +} +``` + +Alternatively, install explicitly from npm: ```bash clawdbot plugins install @clawdbot/voice-call @@ -38,15 +51,6 @@ clawdbot plugins install @clawdbot/voice-call Restart the Gateway afterwards. -### Option B: install from a local folder (dev, no copying) - -```bash -clawdbot plugins install ./extensions/voice-call -cd ./extensions/voice-call && pnpm install -``` - -Restart the Gateway afterwards. - ## Config Set config under `plugins.entries.voice-call.config`: diff --git a/docs/plugins/zalouser.md b/docs/plugins/zalouser.md index d2f2bef6c..fdd268405 100644 --- a/docs/plugins/zalouser.md +++ b/docs/plugins/zalouser.md @@ -19,9 +19,22 @@ This plugin runs **inside the Gateway process**. If you use a remote Gateway, install/configure it on the **machine running the Gateway**, then restart the Gateway. -## Install +## Plugin setup -### Option A: install from npm +Zalo Personal is bundled with Clawdbot. Its dependencies are automatically installed on first +load when you enable the plugin. + +To enable, add `zalouser` to your plugins allow list: + +```json5 +{ + plugins: { + allow: ["zalouser"] + } +} +``` + +Alternatively, install explicitly from npm: ```bash clawdbot plugins install @clawdbot/zalouser @@ -29,15 +42,6 @@ clawdbot plugins install @clawdbot/zalouser Restart the Gateway afterwards. -### Option B: install from a local folder (dev) - -```bash -clawdbot plugins install ./extensions/zalouser -cd ./extensions/zalouser && pnpm install -``` - -Restart the Gateway afterwards. - ## Prerequisite: zca-cli The Gateway machine must have `zca` on `PATH`: diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md index 244757a48..ab4737821 100644 --- a/docs/reference/RELEASING.md +++ b/docs/reference/RELEASING.md @@ -81,14 +81,24 @@ When the operator says “release”, immediately do this preflight (no extra qu ## Plugin publish scope (npm) -We only publish **existing npm plugins** under the `@clawdbot/*` scope. Bundled -plugins that are not on npm stay **disk-tree only** (still shipped in -`extensions/**`). +We publish some plugins to npm under the `@clawdbot/*` scope for users who prefer +explicit installation. However, **all plugins are bundled** in `extensions/**` and +their dependencies are **auto-installed on first load** when enabled. -Process to derive the list: -1) `npm search @clawdbot --json` and capture the package names. -2) Compare with `extensions/*/package.json` names. -3) Publish only the **intersection** (already on npm). +### Bundled plugins with auto-install + +Bundled plugins ship with source code but not `node_modules`. When a bundled plugin +is enabled and has external dependencies (not `clawdbot` workspace refs), the loader +automatically runs `npm install` in the plugin directory on first load. + +This means users don't need to manually install plugins or their dependencies—they +just enable the plugin in config and restart. + +### npm-published plugins (optional) + +Some plugins are also published to npm for users who prefer explicit installation +via `clawdbot plugins install @clawdbot/xxx`. This copies the plugin to +`~/.clawdbot/extensions/` and runs `npm install` there. Current npm plugin list (update as needed): - @clawdbot/bluebubbles @@ -103,5 +113,14 @@ Current npm plugin list (update as needed): - @clawdbot/zalo - @clawdbot/zalouser -Release notes must also call out **new optional bundled plugins** that are **not -on by default** (example: `tlon`). +To add a new plugin to npm: +1) First-publish manually: `cd extensions/ && npm publish --access public` +2) Add to the list above +3) Future releases will include it automatically + +### Bundled-only plugins + +Some plugins are bundled but not published to npm (e.g., `tlon`). These work via +the auto-install mechanism—users just enable them in config. + +Release notes should call out **new bundled plugins** so users know they're available. diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index 931c15d59..18b63cd4e 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -1,3 +1,4 @@ +import { spawnSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; @@ -43,6 +44,69 @@ const registryCache = new Map(); const defaultLogger = () => createSubsystemLogger("plugins"); +/** + * Ensures plugin dependencies are installed for bundled plugins. + * Bundled plugins ship with source but not node_modules; this installs + * deps on first load if the plugin has dependencies in package.json. + */ +function ensureBundledPluginDeps(params: { + rootDir: string; + pluginId: string; + logger: PluginLogger; +}): { ok: boolean; error?: string } { + const { rootDir, pluginId, logger } = params; + const packageJsonPath = path.join(rootDir, "package.json"); + + if (!fs.existsSync(packageJsonPath)) { + return { ok: true }; // No package.json, nothing to install + } + + let manifest: { dependencies?: Record }; + try { + manifest = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")); + } catch { + return { ok: true }; // Can't parse, skip + } + + const deps = manifest.dependencies ?? {}; + const depNames = Object.keys(deps).filter( + (name) => !name.startsWith("clawdbot") && name !== "clawdbot", + ); + + if (depNames.length === 0) { + return { ok: true }; // No external dependencies + } + + // Check if first dependency exists in node_modules + const firstDep = depNames[0]; + const depPath = path.join(rootDir, "node_modules", ...firstDep.split("/")); + + if (fs.existsSync(depPath)) { + return { ok: true }; // Dependencies already installed + } + + logger.info?.(`[plugins] ${pluginId}: installing dependencies…`); + + try { + const result = spawnSync("npm", ["install", "--omit=dev", "--silent"], { + cwd: rootDir, + encoding: "utf-8", + timeout: 300_000, + stdio: ["ignore", "pipe", "pipe"], + }); + + if (result.status !== 0) { + const errorMsg = result.stderr?.trim() || result.stdout?.trim() || "unknown error"; + return { ok: false, error: `npm install failed: ${errorMsg}` }; + } + + logger.info?.(`[plugins] ${pluginId}: dependencies installed`); + return { ok: true }; + } catch (err) { + return { ok: false, error: `failed to install dependencies: ${String(err)}` }; + } +} + const resolvePluginSdkAlias = (): string | null => { try { const modulePath = fileURLToPath(import.meta.url); @@ -282,6 +346,29 @@ export function loadClawdbotPlugins(options: PluginLoadOptions = {}): PluginRegi continue; } + // Ensure bundled plugin dependencies are installed before loading + if (candidate.origin === "bundled") { + const depsResult = ensureBundledPluginDeps({ + rootDir: candidate.rootDir, + pluginId, + logger, + }); + if (!depsResult.ok) { + logger.error(`[plugins] ${record.id} failed to install deps: ${depsResult.error}`); + record.status = "error"; + record.error = depsResult.error ?? "failed to install dependencies"; + registry.plugins.push(record); + seenIds.set(pluginId, candidate.origin); + registry.diagnostics.push({ + level: "error", + pluginId: record.id, + source: record.source, + message: record.error, + }); + continue; + } + } + let mod: ClawdbotPluginModule | null = null; try { mod = jiti(candidate.source) as ClawdbotPluginModule;