UNPKG

@lark-project/cli

Version:

飞书项目插件开发工具

108 lines (107 loc) 6.63 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.addPermCommand = void 0; // ⚠️ 改命令名 / flag / alias 时,同步 grep `lpm` skills/ .claude/skills/ 修文档引用 const path_1 = __importDefault(require("path")); const types_1 = require("../../types"); const logger_1 = require("../../utils/logger"); const validate_tools_1 = require("../../utils/validate-tools"); const run_script_1 = __importDefault(require("../../utils/run-script")); /** * 解析 `--plugin/--site-domain`(工程目录外定位插件用)。 * 给了 `--plugin` 但格式非法 → fail-fast。返回归一后的两个可选值。 */ function resolveTargetFlags(opts) { var _a, _b; const pluginId = ((_a = opts.plugin) === null || _a === void 0 ? void 0 : _a.trim()) || undefined; const siteDomain = ((_b = opts.siteDomain) === null || _b === void 0 ? void 0 : _b.trim()) || undefined; if (pluginId && !(0, validate_tools_1.isValidPluginId)(pluginId)) { logger_1.logger.error(`The --plugin "${pluginId}" is not valid (expected MII_ + 16 uppercase alphanumeric chars, e.g. MII_69F2F73D121E0BCC).`); process.exit(1); } return { pluginId, siteDomain }; } const TARGET_FLAG_HELP = 'Target plugin id (MII_…) — use to run OUTSIDE a plugin project (e.g. from a backend repo). ' + 'If omitted, the plugin is read from plugin.config.json, then from the global registry of plugins you have worked on.'; const SITE_DOMAIN_FLAG_HELP = 'Site origin owning the plugin (e.g. https://project.feishu.cn). Required together with --plugin.'; function addPermCommand(program) { const perm = program .command('perm') .description("Manage this plugin's OpenAPI permission scopes. Reads pluginId/siteDomain from plugin.config.json when run inside a plugin project; otherwise pass --plugin/--site-domain (or rely on the global registry)."); perm .command('list') .description("List this plugin's OpenAPI permission scopes as a derived view. Prints JSON on stdout: " + '{ appType, granted: [{scope,name,openapis:[{key,name,url}]}] — already-granted scopes you can call now, ' + 'applicable: [{scope,name,openapis}] — scopes you can still apply for (omitted on ai_node/ai_field: fixed at create time) }. ' + 'Feasibility (plan): is the OpenAPI you need anywhere in granted ∪ applicable? Implementation: needed scope in granted = ready, in applicable = `perm apply` it. ' + 'Pass --raw for the backend\'s original { perm_info, perm_meta }. Works outside a plugin project via --plugin/--site-domain or the global registry (read-only, no pluginSecret needed).') .option('--raw', "Print the backend's original { perm_info, perm_meta } instead of the derived { granted, applicable } view.") .option('--plugin <plugin-id>', TARGET_FLAG_HELP) .option('--site-domain <site-domain>', SITE_DOMAIN_FLAG_HELP) .action(opts => { const { pluginId, siteDomain } = resolveTargetFlags(opts); (0, run_script_1.default)(path_1.default.join(__dirname, '../dispatcher'), [ '--command', types_1.ECommandName.perm, '--payload', JSON.stringify({ action: 'list', raw: Boolean(opts.raw), pluginId, siteDomain }), ]); }); perm .command('check') .description('Check whether the OpenAPIs a feature needs are permitted. Give the interfaces (resource key / short name / Chinese name) via --apis; ' + 'the CLI reverse-looks-up each to its owning scope and compares against granted. Prints JSON on stdout: ' + '{ appType, satisfied: [api] — owning scope already granted, needApply: [{scope,name,apis}] — scope not granted but applicable (run `lpm perm apply --scopes <scope>`), ' + 'infeasible: [{api,reason}] — AI plugin missing a fixed-set scope, unknown: [api] — no such OpenAPI in this plugin\'s catalog, ' + 'ambiguous: [{api,candidates}] — Chinese name maps to multiple scopes, pass a resource key to disambiguate }. ' + 'Works outside a plugin project via --plugin/--site-domain or the global registry.') .requiredOption('--apis <apis>', 'Comma-separated OpenAPI identifiers, e.g. --apis OAPIGetWorkItemsByIds,获取用户详情') .option('--plugin <plugin-id>', TARGET_FLAG_HELP) .option('--site-domain <site-domain>', SITE_DOMAIN_FLAG_HELP) .action(options => { const { apis } = options; const list = apis .split(',') .map(s => s.trim()) .filter(Boolean); if (list.length === 0) { logger_1.logger.error('--apis must contain at least one OpenAPI identifier.'); process.exit(1); } const { pluginId, siteDomain } = resolveTargetFlags(options); (0, run_script_1.default)(path_1.default.join(__dirname, '../dispatcher'), [ '--command', types_1.ECommandName.perm, '--payload', JSON.stringify({ action: 'check', apis: list, pluginId, siteDomain }), ]); }); perm .command('apply') .description('Apply for OpenAPI permission scopes (already-granted ones are skipped). Prints JSON { perm_info: [{key,status}] } on stdout — the plugin\'s checked scopes after the apply (status=1 = granted; otherwise the scope still needs approval in the developer console). Run `lpm perm list` first to see available scope keys.') .requiredOption('--scopes <scopes>', 'Comma-separated scope keys, e.g. --scopes openapi:work_item:read,openapi:work_item:write') .option('--plugin <plugin-id>', TARGET_FLAG_HELP) .option('--site-domain <site-domain>', SITE_DOMAIN_FLAG_HELP) .action(options => { const { scopes } = options; const list = scopes .split(',') .map(s => s.trim()) .filter(Boolean); if (list.length === 0) { logger_1.logger.error('--scopes must contain at least one scope key.'); process.exit(1); } const { pluginId, siteDomain } = resolveTargetFlags(options); (0, run_script_1.default)(path_1.default.join(__dirname, '../dispatcher'), [ '--command', types_1.ECommandName.perm, '--payload', JSON.stringify({ action: 'apply', scopes: list, pluginId, siteDomain }), ]); }); } exports.addPermCommand = addPermCommand;