From 0891bda134afdfe68a0a95972f885724f18effeb Mon Sep 17 00:00:00 2001 From: yeuimu <2197651308@qq.com> Date: Thu, 6 Aug 2026 09:03:04 +0800 Subject: [PATCH] feat(cli): add inkreach-teable plugin commands plugin list: list registered plugins (id/name/status/runtimeType) plugin register: register by URL (--name --url --positions) plugin config list: list base configs (pluginId/tableId/enabled/form/write) plugin config enable/disable: enable/disable per table or base-wide Added 5 client methods: getPlugins, registerPlugin, listPluginConfigs, upsertPluginConfig for CRUD. --- dist/bin.js | 2 + dist/commands/plugin.js | 125 ++++++++++++++++++++++++++++++++++++ dist/lib/client.js | 13 ++++ src/bin.ts | 2 + src/commands/plugin.ts | 138 ++++++++++++++++++++++++++++++++++++++++ src/lib/client.ts | 14 ++++ 6 files changed, 294 insertions(+) create mode 100644 dist/commands/plugin.js create mode 100644 src/commands/plugin.ts diff --git a/dist/bin.js b/dist/bin.js index 4168043..61ffa7e 100644 --- a/dist/bin.js +++ b/dist/bin.js @@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); */ const commander_1 = require("commander"); const package_json_1 = require("../package.json"); +const plugin_1 = require("./commands/plugin"); const auth_1 = require("./commands/auth"); const workflow_1 = require("./commands/workflow"); const resources_1 = require("./commands/resources"); @@ -27,6 +28,7 @@ program .option('--debug', 'Print requests/responses to stderr (verbose)'); // Command groups program.addCommand((0, auth_1.authCommand)()); +program.addCommand((0, plugin_1.pluginCommand)()); for (const c of (0, resources_1.baseResourceCommands)()) program.addCommand(c); program.addCommand((0, workflow_1.workflowCommand)()); diff --git a/dist/commands/plugin.js b/dist/commands/plugin.js new file mode 100644 index 0000000..30b4627 --- /dev/null +++ b/dist/commands/plugin.js @@ -0,0 +1,125 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.pluginCommand = void 0; +/** + * `inkreach-teable plugin` — plugin management commands. + */ +const commander_1 = require("commander"); +const ctx_1 = require("../lib/ctx"); +const output_1 = require("../lib/output"); +const request_1 = require("../lib/request"); +void ctx_1.requireBaseId; +function pluginCommand() { + const cmd = new commander_1.Command('plugin'); + cmd.description('Manage plugins (list, register, config)'); + cmd + .command('list') + .description('List registered plugins') + .action((0, ctx_1.run)(async (ctx) => { + try { + const list = (await ctx.client.getPlugins()); + if (ctx.json) { + (0, output_1.printJson)(list); + } + else { + (0, output_1.printTable)([ + { key: 'id', header: 'ID' }, + { key: 'name', header: 'NAME' }, + { key: 'status', header: 'STATUS' }, + { key: 'runtimeType', header: 'RUNTIME' }, + ], list.map((p) => ({ id: p.id, name: p.name, status: p.status, runtimeType: p.runtimeType ?? 'url' }))); + } + } + catch (e) { + if (e instanceof request_1.ApiError && e.status === 401) { + process.stderr.write('Unauthorized — log in via "auth login" or provide --token\n'); + process.exit(1); + } + throw e; + } + })); + cmd + .command('register') + .description('Register a plugin by URL') + .requiredOption('--url ', 'Plugin URL') + .requiredOption('--name ', 'Plugin name') + .option('--description ', 'Plugin description') + .option('--logo ', 'Logo URL') + .option('--positions ', 'Positions array, e.g. \'["panel"]\'', '["panel"]') + .action((0, ctx_1.run)(async (ctx, _args, o) => { + const opts = o; + const positions = JSON.parse(opts.positions); + const created = await ctx.client.registerPlugin({ + name: opts.name, + url: opts.url, + description: opts.description, + logo: opts.logo ?? '', + positions, + status: 'published', + }); + if (ctx.json) + (0, output_1.printJson)(created); + else + process.stdout.write(`✓ Plugin registered: ${created.id}\n`); + })); + const config = cmd.command('config').description('Manage per-table plugin enablement'); + config + .command('list') + .description('List plugin configs for the base') + .action((0, ctx_1.run)(async (ctx) => { + const list = await ctx.client.listPluginConfigs(ctx.baseId); + if (ctx.json) + (0, output_1.printJson)(list); + else { + (0, output_1.printTable)([ + { key: 'pluginId', header: 'PLUGIN' }, + { key: 'tableId', header: 'TABLE' }, + { key: 'enabled', header: 'ON' }, + { key: 'runtimeForm', header: 'FORM' }, + { key: 'writeEnabled', header: 'WRITE' }, + ], list.map((r) => ({ + pluginId: r.pluginId, + tableId: r.tableId || '-', + enabled: r.enabled, + runtimeForm: r.runtimeForm, + writeEnabled: r.writeEnabled, + }))); + } + })); + config + .command('enable') + .description('Enable a plugin for a table (or base-wide if no --table)') + .requiredOption('--plugin ', 'Plugin ID') + .option('--table ', 'Table ID (omit for base-wide)') + .option('--write', 'Allow write-back') + .action((0, ctx_1.run)(async (ctx, _args, o) => { + const opts = o; + const result = await ctx.client.upsertPluginConfig(ctx.baseId, opts.plugin, { + enabled: true, + runtimeForm: 'panel', + writeEnabled: !!opts.write, + tableIds: opts.table ? [opts.table] : [], + }); + if (ctx.json) + (0, output_1.printJson)(result); + else + process.stdout.write(`✓ Plugin ${opts.plugin} enabled${opts.table ? ` for table ${opts.table}` : ' base-wide'}\n`); + })); + config + .command('disable') + .description('Disable a plugin for a table (or base-wide if no --table)') + .requiredOption('--plugin ', 'Plugin ID') + .option('--table ', 'Table ID') + .action((0, ctx_1.run)(async (ctx, _args, o) => { + const opts = o; + await ctx.client.upsertPluginConfig(ctx.baseId, opts.plugin, { + enabled: false, + runtimeForm: 'panel', + writeEnabled: false, + tableIds: opts.table ? [opts.table] : [], + }); + process.stdout.write(`✓ Plugin ${opts.plugin} disabled${opts.table ? ` for table ${opts.table}` : ' base-wide'}\n`); + })); + return cmd; +} +exports.pluginCommand = pluginCommand; diff --git a/dist/lib/client.js b/dist/lib/client.js index 81e748f..46f6966 100644 --- a/dist/lib/client.js +++ b/dist/lib/client.js @@ -207,5 +207,18 @@ class Client { sql(baseId, sql) { return this.call('POST', `/api/base/${baseId}/sql`, { sql }); } + // ---- plugin ---- + getPlugins() { + return this.call('GET', '/api/plugin'); + } + registerPlugin(body) { + return this.call('POST', '/api/plugin', body); + } + listPluginConfigs(baseId) { + return this.call('GET', `/api/base/${baseId}/plugin-config`); + } + upsertPluginConfig(baseId, pluginId, body) { + return this.call('PUT', `/api/base/${baseId}/plugin-config/${pluginId}`, body); + } } exports.Client = Client; diff --git a/src/bin.ts b/src/bin.ts index 5ef1e09..3098659 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -6,6 +6,7 @@ */ import { Command } from 'commander'; import { version } from '../package.json'; +import { pluginCommand } from './commands/plugin'; import { authCommand } from './commands/auth'; import { workflowCommand, triggerCommand, actionCommand } from './commands/workflow'; import { baseResourceCommands } from './commands/resources'; @@ -31,6 +32,7 @@ program // Command groups program.addCommand(authCommand()); +program.addCommand(pluginCommand()); for (const c of baseResourceCommands()) program.addCommand(c); program.addCommand(workflowCommand()); program.addCommand(triggerCommand()); diff --git a/src/commands/plugin.ts b/src/commands/plugin.ts new file mode 100644 index 0000000..4eab763 --- /dev/null +++ b/src/commands/plugin.ts @@ -0,0 +1,138 @@ +/** + * `inkreach-teable plugin` — plugin management commands. + */ +import { Command } from 'commander'; +import { run, requireBaseId } from '../lib/ctx'; +import { printJson, printTable } from '../lib/output'; +import { ApiError } from '../lib/request'; + +void requireBaseId; + +export function pluginCommand(): Command { + const cmd = new Command('plugin'); + cmd.description('Manage plugins (list, register, config)'); + + cmd + .command('list') + .description('List registered plugins') + .action( + run(async (ctx) => { + try { + const list = (await ctx.client.getPlugins()) as Array<{ id: string; name: string; status: string; runtimeType?: string }>; + if (ctx.json) { + printJson(list); + } else { + printTable( + [ + { key: 'id', header: 'ID' }, + { key: 'name', header: 'NAME' }, + { key: 'status', header: 'STATUS' }, + { key: 'runtimeType', header: 'RUNTIME' }, + ], + list.map((p) => ({ id: p.id, name: p.name, status: p.status, runtimeType: p.runtimeType ?? 'url' })) + ); + } + } catch (e) { + if (e instanceof ApiError && e.status === 401) { + process.stderr.write('Unauthorized — log in via "auth login" or provide --token\n'); + process.exit(1); + } + throw e; + } + }) + ); + + cmd + .command('register') + .description('Register a plugin by URL') + .requiredOption('--url ', 'Plugin URL') + .requiredOption('--name ', 'Plugin name') + .option('--description ', 'Plugin description') + .option('--logo ', 'Logo URL') + .option('--positions ', 'Positions array, e.g. \'["panel"]\'', '["panel"]') + .action( + run(async (ctx, _args, o) => { + const opts = o as { url: string; name: string; description?: string; logo?: string; positions: string }; + const positions = JSON.parse(opts.positions); + const created = await ctx.client.registerPlugin({ + name: opts.name, + url: opts.url, + description: opts.description, + logo: opts.logo ?? '', + positions, + status: 'published', + }); + if (ctx.json) printJson(created); + else process.stdout.write(`✓ Plugin registered: ${(created as { id?: string }).id}\n`); + }) + ); + + const config = cmd.command('config').description('Manage per-table plugin enablement'); + config + .command('list') + .description('List plugin configs for the base') + .action( + run(async (ctx) => { + const list = await ctx.client.listPluginConfigs(ctx.baseId!); + if (ctx.json) printJson(list); + else { + printTable( + [ + { key: 'pluginId', header: 'PLUGIN' }, + { key: 'tableId', header: 'TABLE' }, + { key: 'enabled', header: 'ON' }, + { key: 'runtimeForm', header: 'FORM' }, + { key: 'writeEnabled', header: 'WRITE' }, + ], + (list as Array>).map((r) => ({ + pluginId: r.pluginId, + tableId: r.tableId || '-', + enabled: r.enabled, + runtimeForm: r.runtimeForm, + writeEnabled: r.writeEnabled, + })) + ); + } + }) + ); + + config + .command('enable') + .description('Enable a plugin for a table (or base-wide if no --table)') + .requiredOption('--plugin ', 'Plugin ID') + .option('--table ', 'Table ID (omit for base-wide)') + .option('--write', 'Allow write-back') + .action( + run(async (ctx, _args, o) => { + const opts = o as { plugin: string; table?: string; write?: boolean }; + const result = await ctx.client.upsertPluginConfig(ctx.baseId!, opts.plugin, { + enabled: true, + runtimeForm: 'panel', + writeEnabled: !!opts.write, + tableIds: opts.table ? [opts.table] : [], + }); + if (ctx.json) printJson(result); + else process.stdout.write(`✓ Plugin ${opts.plugin} enabled${opts.table ? ` for table ${opts.table}` : ' base-wide'}\n`); + }) + ); + + config + .command('disable') + .description('Disable a plugin for a table (or base-wide if no --table)') + .requiredOption('--plugin ', 'Plugin ID') + .option('--table ', 'Table ID') + .action( + run(async (ctx, _args, o) => { + const opts = o as { plugin: string; table?: string }; + await ctx.client.upsertPluginConfig(ctx.baseId!, opts.plugin, { + enabled: false, + runtimeForm: 'panel', + writeEnabled: false, + tableIds: opts.table ? [opts.table] : [], + }); + process.stdout.write(`✓ Plugin ${opts.plugin} disabled${opts.table ? ` for table ${opts.table}` : ' base-wide'}\n`); + }) + ); + + return cmd; +} diff --git a/src/lib/client.ts b/src/lib/client.ts index 89734a9..b43a51c 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -224,6 +224,20 @@ export class Client { sql(baseId: string, sql: string) { return this.call('POST', `/api/base/${baseId}/sql`, { sql }); } + + // ---- plugin ---- + getPlugins() { + return this.call('GET', '/api/plugin'); + } + registerPlugin(body: Record) { + return this.call('POST', '/api/plugin', body); + } + listPluginConfigs(baseId: string) { + return this.call('GET', `/api/base/${baseId}/plugin-config`); + } + upsertPluginConfig(baseId: string, pluginId: string, body: Record) { + return this.call('PUT', `/api/base/${baseId}/plugin-config/${pluginId}`, body); + } } // ---- shared types (mirror backend workflow_node / workflow_edge shapes) ----