Files
inkreach-teable-cli/dist/commands/plugin.js
T
yeuimu 4dc65c2a0c fix(test): update plugin-base-config spec to match findMany implementation
getEffectiveConfig was rewritten in 948a20b88 to fetch all configs via
findMany (Prisma cannot reliably WHERE on nullable String? with ''), but
the spec still mocked the old findFirst call chain — the table-override
test silently fell back to defaults. Mocks now return the config array.
2026-08-14 07:27:22 +00:00

274 lines
11 KiB
JavaScript

"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;
// ---- developer whitelist subcommands (Feature 4) ----
function addDevCommands(cmd) {
const dev = cmd.command('dev').description('Manage plugin developer whitelist (admin)');
dev
.command('list')
.description('List all whitelisted developers')
.action((0, ctx_1.run)(async (ctx) => {
const list = (await ctx.client.listDevelopers());
if (ctx.json)
(0, output_1.printJson)(list);
else if (!list.length)
process.stdout.write('(no developers)\n');
else
(0, output_1.printTable)([
{ key: 'userId', header: 'USER_ID' },
{ key: 'email', header: 'EMAIL' },
{ key: 'createdBy', header: 'ADDED_BY' },
], list);
}));
dev
.command('allow')
.description('Add a user to the developer whitelist (userId or email)')
.argument('<userIdOrEmail>', 'User ID or email')
.action((0, ctx_1.run)(async (ctx, args) => {
const userIdOrEmail = args[0];
await ctx.client.addDeveloper(userIdOrEmail);
process.stdout.write(`✓ Developer added: ${userIdOrEmail}\n`);
}));
dev
.command('revoke')
.description('Remove a user from the developer whitelist (userId or email)')
.argument('<userIdOrEmail>', 'User ID or email')
.action((0, ctx_1.run)(async (ctx, args) => {
const userIdOrEmail = args[0];
await ctx.client.removeDeveloper(userIdOrEmail);
process.stdout.write(`✓ Developer revoked: ${userIdOrEmail}\n`);
}));
}
// ---- install / uninstall subcommands (Feature 1) ----
function addInstallCommands(cmd) {
cmd
.command('install')
.description('Install a plugin into the current base (creates a node)')
.requiredOption('--plugin <id>', 'Plugin ID')
.requiredOption('--name <name>', 'Display name for this node')
.requiredOption('--position <pos>', 'Mount position: base | panel | view | dashboard | contextMenu', 'base')
.option('--storage <json>', 'Initial storage JSON passed to the plugin')
.action((0, ctx_1.run)(async (ctx, _args, o) => {
const opts = o;
const body = {
pluginId: opts.plugin,
name: opts.name,
position: opts.position,
};
if (opts.storage)
body.storage = opts.storage;
const result = (await ctx.client.createInstall(ctx.baseId, body));
if (ctx.json)
(0, output_1.printJson)(result);
else
process.stdout.write(`✓ Plugin installed: ${result.id} (${opts.name} → ${opts.position})\n`);
}));
cmd
.command('uninstall')
.description('Remove a plugin install by its install ID')
.requiredOption('--install <id>', 'Install ID')
.action((0, ctx_1.run)(async (ctx, _args, o) => {
const opts = o;
await ctx.client.deleteInstall(ctx.baseId, opts.install);
process.stdout.write(`✓ Plugin install removed: ${opts.install}\n`);
}));
cmd
.command('installs')
.description('List plugin installs for the current base')
.action((0, ctx_1.run)(async (ctx) => {
const list = (await ctx.client.listInstalls(ctx.baseId));
if (ctx.json)
(0, output_1.printJson)(list);
else if (!list.length)
process.stdout.write('(no installs)\n');
else
(0, output_1.printTable)([
{ key: 'id', header: 'INSTALL_ID' },
{ key: 'pluginId', header: 'PLUGIN' },
{ key: 'name', header: 'NAME' },
{ key: 'position', header: 'POSITION' },
], list);
}));
}
function pluginCommand() {
const cmd = new commander_1.Command('plugin');
cmd.description('Manage plugins (list, register, config, create, install, dev)');
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 <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((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 <id>', 'Plugin ID')
.option('--table <id>', '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 <id>', 'Plugin ID')
.option('--table <id>', '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`);
}));
// ---- CSP apiOrigins management ----
const csp = cmd.command('csp').description('Manage plugin CSP apiOrigins');
csp
.command('list')
.description('Show the union of all plugins\' apiOrigins (effective CSP connect-src extras)')
.action((0, ctx_1.run)(async (ctx) => {
const { origins } = await ctx.client.getCspOrigins();
if (ctx.json)
(0, output_1.printJson)(origins);
else if (!origins.length)
process.stdout.write('(no apiOrigins declared by any plugin)\n');
else
origins.forEach((o) => process.stdout.write(`${o}\n`));
}));
csp
.command('set <pluginId>')
.description('Set the external API origins a plugin may fetch (bundle plugins included)')
.requiredOption('--origins <json>', 'JSON array of origins, e.g. \'["http://192.168.124.92:8001"]\'')
.option('--clear', 'Remove all origins (pass "[]" works too)')
.action((0, ctx_1.run)(async (ctx, args, o) => {
const opts = o;
const pluginId = args[0];
let origins;
try {
const parsed = JSON.parse(opts.origins);
if (!Array.isArray(parsed) || parsed.some((x) => typeof x !== 'string')) {
throw new Error('not a string array');
}
origins = parsed;
}
catch {
process.stderr.write(`✗ --origins must be a JSON string array, got: ${opts.origins}\n`);
process.exitCode = 1;
return;
}
if (opts.clear)
origins = [];
// updatePlugin requires name + positions (full-vo update): fetch current values.
const cur = await ctx.client.getPlugin(pluginId);
await ctx.client.updatePlugin(pluginId, {
name: cur.name,
positions: cur.positions,
description: cur.description,
detailDesc: cur.detailDesc,
url: cur.url,
logo: cur.logo,
helpUrl: cur.helpUrl,
apiOrigins: origins,
});
process.stdout.write(`✓ apiOrigins for ${pluginId} set to: ${JSON.stringify(origins)}\n`);
}));
// Wire feature subcommands
addDevCommands(cmd);
addInstallCommands(cmd);
return cmd;
}
exports.pluginCommand = pluginCommand;