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.
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -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 <url>', 'Plugin URL')
|
||||
.requiredOption('--name <name>', 'Plugin name')
|
||||
.option('--description <desc>', 'Plugin description')
|
||||
.option('--logo <url>', 'Logo URL')
|
||||
.option('--positions <json>', '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<Record<string, unknown>>).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 <id>', 'Plugin ID')
|
||||
.option('--table <id>', '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 <id>', 'Plugin ID')
|
||||
.option('--table <id>', '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;
|
||||
}
|
||||
@@ -224,6 +224,20 @@ export class Client {
|
||||
sql(baseId: string, sql: string) {
|
||||
return this.call<unknown[]>('POST', `/api/base/${baseId}/sql`, { sql });
|
||||
}
|
||||
|
||||
// ---- plugin ----
|
||||
getPlugins() {
|
||||
return this.call<unknown[]>('GET', '/api/plugin');
|
||||
}
|
||||
registerPlugin(body: Record<string, unknown>) {
|
||||
return this.call<unknown>('POST', '/api/plugin', body);
|
||||
}
|
||||
listPluginConfigs(baseId: string) {
|
||||
return this.call<unknown[]>('GET', `/api/base/${baseId}/plugin-config`);
|
||||
}
|
||||
upsertPluginConfig(baseId: string, pluginId: string, body: Record<string, unknown>) {
|
||||
return this.call<unknown>('PUT', `/api/base/${baseId}/plugin-config/${pluginId}`, body);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- shared types (mirror backend workflow_node / workflow_edge shapes) ----
|
||||
|
||||
Reference in New Issue
Block a user