@lark-project/cli
Version:
飞书项目插件开发工具
108 lines (107 loc) • 6.63 kB
JavaScript
;
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;