Plugin Example: Lark Docs
This article demonstrates a complete plugin example, implemented based on the Lark platform. The example covers plugin features, configuration, code structure, interface implementation, extension points, and more, helping you understand how to develop similar integration plugins on the Xpert AI Platform.
Backgroundโ
The Xpert AI plugin system allows developers to extend platform functionality via plugins. Using Lark Docs as an example, we aim to achieve the following scenarios:
- System Integration: Connect Lark app credentials such as AppID and AppSecret for authentication.
- Document Loading: Retrieve document content from Lark folders or document APIs.
- Knowledge Management: Convert documents into structured content usable by Xpert AI's knowledge base and agents.
- Extension Strategies: Implement extension points for integration, document sources, and document transformation via strategy interfaces.
The end result: Once users configure Lark integration info, Xpert AI can automatically read content from Lark Docs and include it in AI conversations and the knowledge base.
1. Clone the Plugin Templateโ
We provide a starter template for plugin development to quickly build new plugins:
git clone git@github.com:xpert-ai/xpert-plugins-starter.git lark
cd lark
Install dependencies:
npm install
Use nx
to create a new plugin library. Here, packages/lark
is the plugin code directory, and @xpert-ai/plugin-lark
is the npm package name:
npx nx g @nx/js:lib packages/lark \
--publishable \
--importPath=@xpert-ai/plugin-lark \
--bundler=tsc \
--unitTestRunner=jest \
--linter=eslint
Confirm plugin info:
npx nx show project @xpert-ai/plugin-lark
โ This generates a plugin named
@xpert-ai/plugin-lark
that will handle all Lark Docs integration features.
2. Plugin Entry Codeโ
The plugin entry code mainly includes meta info, config, and lifecycle functions (register/onStart/onStop).
import { z } from 'zod';
import { type XpertPlugin } from '@xpert-ai/plugin-sdk';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { initI18n } from './lib/i18n.js';
import { LarkModule } from './lib/lark.module.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const ConfigSchema = z.object({});
const plugin: XpertPlugin<z.infer<typeof ConfigSchema>> = {
meta: {
name: '@xpert-ai/plugin-lark',
version: '1.0.0',
category: 'doc-source',
displayName: 'Lark Plugin',
description: 'Integrate Lark functionality',
keywords: ['lark', 'feishu', 'document source'],
author: 'Xpert AI team',
},
config: {
schema: ConfigSchema,
},
register(ctx) {
ctx.logger.log('register lark plugin');
initI18n(join(__dirname, '../src'));
return { module: LarkModule, global: true };
},
async onStart(ctx) {
ctx.logger.log('lark plugin started');
},
async onStop(ctx) {
ctx.logger.log('lark plugin stopped');
},
};
export default plugin;
Key Pointsโ
- meta: Defines basic plugin info (name, version, category, keywords, etc.).
- config: Describes the plugin config schema, allowing users to input config in the UI (reserved for future use).
- register: Registers
LarkModule
, mounting the plugin to the NestJS module system. - onStart/onStop: Plugin lifecycle callbacks.
3. NestJS Module Structureโ
The plugin is essentially a NestJS module (LarkModule
), which registers various strategy classes, controllers, or services.
import chalk from 'chalk';
import { XpertServerPlugin, IOnPluginBootstrap, IOnPluginDestroy } from '@xpert-ai/plugin-sdk';
import { LarkIntegrationStrategy } from './integration.strategy.js';
import { LarkController } from './lark.controller.js';
import { LarkSourceStrategy } from './source.strategy.js';
import { LarkDocTransformerStrategy } from './transformer.strategy.js';
@XpertServerPlugin({
controllers: [LarkController],
providers: [
LarkIntegrationStrategy,
LarkSourceStrategy,
LarkDocTransformerStrategy
]
})
export class LarkModule implements IOnPluginBootstrap, IOnPluginDestroy {
onPluginBootstrap(): void {
console.log(chalk.green(`${LarkModule.name} is being bootstrapped...`));
}
onPluginDestroy(): void {
console.log(chalk.green(`${LarkModule.name} is being destroyed...`));
}
}
โ The
Strategy
classes are extension points, for example:
LarkIntegrationStrategy
: Handles integration with the Lark system.LarkSourceStrategy
: Defines how to load documents from Lark.LarkDocTransformerStrategy
: Defines how to transform documents into a format usable by the knowledge base.
4. API Interfacesโ
Controllers expose the plugin's REST APIs. For example, testing the Lark connection.
@Post('test')
async connect(@Body() integration: IIntegration) {
try {
const botInfo = await this.integrationStrategy.validateConfig(integration.options)
return integration
} catch (err) {
throw new ForbiddenException('Credentials failed')
}
}
When calling the /lark/test
endpoint (currently the default path for testing integration connectivity, will be unified later), the system validates the configuration (checks if AppID/AppSecret are valid).
5. Plugin Extension Pointsโ
5.1 System Integration Extensionโ
System integration strategy LarkIntegrationStrategy
handles Lark app credential validation and bot info retrieval.
Key logic:
async validateConfig(config: TLarkIntegrationConfig) {
if (!config.appId || !config.appSecret) {
throw new Error('App ID and Secret required')
}
const larkClient = new LarkClient({ options: config } as IIntegration)
const botInfo = await larkClient.getBotInfo()
if (!botInfo) throw new ForbiddenException('Bot permission denied')
return botInfo
}
โ Here,
LarkClient
wraps Lark OpenAPI requests.
5.2 Document Source Extensionโ
Document source strategy LarkSourceStrategy
defines how to fetch document lists from Lark.
- Config params: folder token, document type (docx/sheet/file, etc.).
- Loading logic: Calls
client.listDriveFiles(folderToken)
, returnsDocument[]
.
5.3 Document Transformation Extensionโ
Document transformation strategy LarkDocTransformerStrategy
defines how to convert document content into knowledge base entries.
- Calls Lark API to get document body.
- Splits body into
Document
objects (for vectorization). - Outputs in
IKnowledgeDocument
format.
5.4 Client Wrapperโ
All API requests are wrapped by LarkClient
, which unifies @larksuiteoapi/node-sdk
functionality and provides methods like:
getBotInfo()
Get bot infolistDriveFiles(folderToken)
List folder contentsgetDocumentContent(docToken)
Get document bodygetAllDocsInFolder(folderToken)
Recursively get all documents
This way, the strategy layer only needs to call client methods without worrying about API details.
6. Development Workflow Summaryโ
Prepare template: Clone the plugin template and generate the project.
Write entry code: Define plugin meta, config, and lifecycle.
Build module: Register controllers and strategy classes in
LarkModule
.Implement strategies:
- Integration strategy: Handle AppID/Secret validation.
- Source strategy: Fetch documents from Lark.
- Transformation strategy: Convert documents to knowledge base content.
Wrap client: Manage Lark API calls in a unified way.
Test API: Use
/lark/test
to verify integration.
7. Build, Release, and npm Publish Workflowโ
After developing the @xpert-ai/plugin-lark
plugin, you can use Nx's build and release toolchain to build and publish it to npm. The process includes:
7.1 Build the Pluginโ
Run the build command:
npx nx build @xpert-ai/plugin-lark
This uses Nx's builder (TypeScript compiler
tsc
by default) to compile source code to JavaScript.Build output goes to:
packages/lark/dist
The output directory contains the publishable artifacts (
index.js
, type declarations.d.ts
, etc.).
7.2 Version Managementโ
To ensure proper npm package iteration, maintain the plugin version number. It's recommended to use Nx's release workflow for automatic version management:
Auto bump versionโ
npx nx release patch @xpert-ai/plugin-lark
patch
: Patch version (e.g. 1.0.0 โ 1.0.1)minor
: Minor version (e.g. 1.0.0 โ 1.1.0)major
: Major version (e.g. 1.0.0 โ 2.0.0)
This command will automatically:
- Update the
version
inpackage.json
. - Update
CHANGELOG.md
. - Generate the corresponding Git tag.
If not using
nx release
, you can manually edit theversion
field inpackage.json
.
7.3 Publish to npmโ
After building and bumping the version, run the publish command:
npx nx run @xpert-ai/plugin-lark:nx-release-publish --access public --otp=<one-time-password-if-needed>
--access public
Specifies the package as public (suitable for packages under the@xpert-ai
npm scope).--otp=<code>
If your npm account has 2FA enabled, enter a one-time password (OTP).
โ ๏ธ Before publishing, make sure the following fields in
package.json
are correct:
main
: Points to the build output entry, e.g."dist/index.js"
types
: Points to type declarations, e.g."dist/index.d.ts"
files
: Only includes files to be published (e.g."dist/**/*"
)publishConfig
: Can define default publish access (e.g."access": "public"
)
7.4 Final Workflow Exampleโ
Summing up, the full workflow to publish @xpert-ai/plugin-lark
is:
# 1. Build the plugin
npx nx build @xpert-ai/plugin-lark
# 2. Auto bump version (patch)
npx nx release patch @xpert-ai/plugin-lark
# 3. Publish to npm
npx nx run @xpert-ai/plugin-lark:nx-release-publish --access public --otp=<one-time-password-if-needed>
Once successful, you'll see the latest @xpert-ai/plugin-lark
version on npm. ๐
8. Final Usageโ
After plugin development, you can use it in the Xpert AI platform to:
- Configure Lark AppID/AppSecret
- Select document folders and types
- Automatically load Lark Docs content
- Convert documents into knowledge base entries, supporting AI conversations and agent analysis