@zdsj/pbms-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # PBMS CLI
2
+
3
+ 项目预算管理系统命令行工具。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ cd cli
9
+ npm install
10
+
11
+ # 可选:全局链接,安装后可在任意目录使用 pbms 命令
12
+ npm link
13
+ ```
14
+
15
+ 不链接也可以直接运行:
16
+
17
+ ```bash
18
+ node bin/pbms.js --help
19
+ ```
20
+
21
+ ## 配置
22
+
23
+ 首次使用需要配置服务端地址和 API Key:
24
+
25
+ ```bash
26
+ pbms config set base-url https://your-host/api
27
+ pbms config set api-key sk_xxxxxxxx
28
+ ```
29
+
30
+ 查看配置:
31
+
32
+ ```bash
33
+ pbms config get
34
+ ```
35
+
36
+ 配置文件保存在 `~/.pbms/config.json`。
37
+
38
+ ## 命令一览
39
+
40
+ ### 仪表盘
41
+
42
+ ```bash
43
+ pbms dashboard
44
+ ```
45
+
46
+ ### 公司预算(GSYSK)
47
+
48
+ ```bash
49
+ pbms budget list [--keyword <kw>] [--type <yslx>] [--dept <gsbm>] [--page <p>] [--size <s>]
50
+ pbms budget get <id>
51
+ pbms budget create --data '{"ysbh":"...","ysmc":"..."}'
52
+ pbms budget update <id> --data '{"ysmc":"..."}'
53
+ pbms budget delete <id>
54
+ pbms budget export [--output file.xlsx]
55
+ ```
56
+
57
+ ### 内部项目(NBXMK)
58
+
59
+ ```bash
60
+ pbms internal list [--keyword <kw>] [--phase <xmjd>] [--group <yfz>] [--page <p>] [--size <s>]
61
+ pbms internal get <id>
62
+ pbms internal create --data '{"xmbh":"...","xmmc":"..."}'
63
+ pbms internal update <id> --data '{"xmmc":"..."}'
64
+ pbms internal delete <id>
65
+ pbms internal export [--output file.xlsx]
66
+ ```
67
+
68
+ ### 外部项目(WBXMK)
69
+
70
+ ```bash
71
+ pbms external list [--keyword <kw>] [--phase <xmjd>] [--customerType <khlx>] [--sales <fzxs>] [--page <p>] [--size <s>]
72
+ pbms external get <id>
73
+ pbms external create --data '{"xmbh":"...","xmmc":"..."}'
74
+ pbms external update <id> --data '{"xmmc":"..."}'
75
+ pbms external delete <id>
76
+ pbms external export [--output file.xlsx]
77
+ ```
78
+
79
+ ### 审计日志
80
+
81
+ ```bash
82
+ pbms log list [--keyword <kw>] [--table <table>] [--action <action>] [--page <p>] [--size <s>]
83
+ pbms log get <id>
84
+ ```
85
+
86
+ ### 预算同步
87
+
88
+ ```bash
89
+ pbms sync
90
+ pbms sync --delayed
91
+ ```
92
+
93
+ ## 开发
94
+
95
+ ```bash
96
+ npm run start -- <command>
97
+ ```
98
+
99
+ ## 依赖
100
+
101
+ - Node.js >= 18
package/bin/pbms.js ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+
6
+ process.on('unhandledRejection', (err) => {
7
+ const message = err?.response?.data?.message || err?.message || '未知错误';
8
+ console.error(chalk.red(`错误: ${message}`));
9
+ process.exit(1);
10
+ });
11
+ import { registerConfigCommand } from '../src/commands/config.js';
12
+ import { registerDashboardCommand } from '../src/commands/dashboard.js';
13
+ import { registerBudgetCommand } from '../src/commands/budget.js';
14
+ import { registerInternalCommand } from '../src/commands/internal.js';
15
+ import { registerExternalCommand } from '../src/commands/external.js';
16
+ import { registerLogCommand } from '../src/commands/log.js';
17
+ import { registerSyncCommand } from '../src/commands/sync.js';
18
+ import { readFileSync } from 'node:fs';
19
+ import { fileURLToPath } from 'node:url';
20
+ import path from 'node:path';
21
+
22
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
23
+ const pkg = JSON.parse(readFileSync(path.join(__dirname, '../package.json'), 'utf8'));
24
+
25
+ const program = new Command();
26
+
27
+ program
28
+ .name('pbms')
29
+ .description('项目预算管理系统命令行工具')
30
+ .version(pkg.version);
31
+
32
+ registerConfigCommand(program);
33
+ registerDashboardCommand(program);
34
+ registerBudgetCommand(program);
35
+ registerInternalCommand(program);
36
+ registerExternalCommand(program);
37
+ registerLogCommand(program);
38
+ registerSyncCommand(program);
39
+
40
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@zdsj/pbms-cli",
3
+ "version": "0.1.0",
4
+ "description": "项目预算管理系统命令行工具",
5
+ "type": "module",
6
+ "bin": {
7
+ "pbms": "./bin/pbms.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node bin/pbms.js",
11
+ "test": "node --test"
12
+ },
13
+ "keywords": [
14
+ "cli",
15
+ "budget",
16
+ "management"
17
+ ],
18
+ "author": "",
19
+ "license": "MIT",
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "engines": {
24
+ "node": ">=18.0.0"
25
+ },
26
+ "dependencies": {
27
+ "@inquirer/prompts": "^7.0.0",
28
+ "axios": "^1.7.9",
29
+ "chalk": "^5.4.1",
30
+ "cli-table3": "^0.6.5",
31
+ "commander": "^13.0.0",
32
+ "ora": "^8.1.1"
33
+ }
34
+ }
package/src/api.js ADDED
@@ -0,0 +1,57 @@
1
+ import axios from 'axios';
2
+ import { loadConfig } from './config.js';
3
+ import chalk from 'chalk';
4
+
5
+ let client = null;
6
+
7
+ export function getClient() {
8
+ if (client) return client;
9
+
10
+ const config = loadConfig();
11
+ client = axios.create({
12
+ baseURL: config.baseUrl.replace(/\/$/, ''),
13
+ timeout: 30000,
14
+ headers: {
15
+ 'Content-Type': 'application/json',
16
+ },
17
+ });
18
+
19
+ client.interceptors.request.use((req) => {
20
+ if (config.apiKey) {
21
+ req.headers['X-API-Key'] = config.apiKey;
22
+ }
23
+ return req;
24
+ });
25
+
26
+ client.interceptors.response.use(
27
+ (res) => res,
28
+ (err) => {
29
+ if (err.response) {
30
+ const { status, data } = err.response;
31
+ const message = data?.message || data?.error || `HTTP ${status}`;
32
+ if (status === 401) {
33
+ console.error(chalk.red('鉴权失败:请检查 base-url 和 api-key 是否配置正确'));
34
+ } else {
35
+ console.error(chalk.red(`请求失败: ${message}`));
36
+ }
37
+ } else if (err.request) {
38
+ console.error(chalk.red('无法连接到服务器,请检查 base-url 配置'));
39
+ } else {
40
+ console.error(chalk.red(`请求异常: ${err.message}`));
41
+ }
42
+ throw err;
43
+ }
44
+ );
45
+
46
+ return client;
47
+ }
48
+
49
+ export function resetClient() {
50
+ client = null;
51
+ }
52
+
53
+ export async function request(method, url, options = {}) {
54
+ const c = getClient();
55
+ const res = await c.request({ method, url, ...options });
56
+ return res.data;
57
+ }
@@ -0,0 +1,20 @@
1
+ import { createResourceCommands } from './resource.js';
2
+
3
+ export function registerBudgetCommand(program) {
4
+ createResourceCommands(program, 'budget', '/gsysk', {
5
+ idField: 'id',
6
+ columns: [
7
+ { field: 'id', title: 'ID' },
8
+ { field: 'ysbh', title: '预算编号' },
9
+ { field: 'ysmc', title: '预算名称' },
10
+ { field: 'yslx', title: '预算类型' },
11
+ { field: 'ysje', title: '预算金额' },
12
+ { field: 'yyje', title: '已用金额' },
13
+ { field: 'gsbm', title: '归属部门' },
14
+ ],
15
+ searchParams: [
16
+ { name: 'type', param: 'yslx', description: '预算类型' },
17
+ { name: 'dept', param: 'gsbm', description: '归属部门' },
18
+ ],
19
+ });
20
+ }
@@ -0,0 +1,46 @@
1
+ import { loadConfig, saveConfig, getConfigPath } from '../config.js';
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+
5
+ export function registerConfigCommand(program) {
6
+ const configCmd = program.command('config').description('CLI 配置管理');
7
+
8
+ configCmd
9
+ .command('set <key> <value>')
10
+ .description('设置配置项(base-url, api-key)')
11
+ .action(async (key, value) => {
12
+ const validKeys = ['base-url', 'api-key'];
13
+ if (!validKeys.includes(key)) {
14
+ console.error(chalk.red(`未知配置项: ${key}。有效项: ${validKeys.join(', ')}`));
15
+ process.exit(1);
16
+ }
17
+
18
+ const config = loadConfig();
19
+ if (key === 'base-url') {
20
+ config.baseUrl = value;
21
+ } else if (key === 'api-key') {
22
+ config.apiKey = value;
23
+ }
24
+ saveConfig(config);
25
+ console.log(chalk.green(`已设置 ${key}`));
26
+ });
27
+
28
+ configCmd
29
+ .command('get [key]')
30
+ .description('查看配置项')
31
+ .action(async (key) => {
32
+ const config = loadConfig();
33
+ if (key) {
34
+ const displayKey = key === 'base-url' ? 'baseUrl' : key === 'api-key' ? 'apiKey' : key;
35
+ if (displayKey === 'apiKey') {
36
+ console.log(config[displayKey] ? '***已设置***' : '(未设置)');
37
+ } else {
38
+ console.log(config[displayKey] ?? '');
39
+ }
40
+ } else {
41
+ console.log('配置文件路径:', getConfigPath());
42
+ console.log('base-url:', config.baseUrl);
43
+ console.log('api-key:', config.apiKey ? '***已设置***' : '(未设置)');
44
+ }
45
+ });
46
+ }
@@ -0,0 +1,33 @@
1
+ import { request } from '../api.js';
2
+ import { formatApiResponse } from '../utils/format.js';
3
+ import Table from 'cli-table3';
4
+ import chalk from 'chalk';
5
+
6
+ export function registerDashboardCommand(program) {
7
+ program
8
+ .command('dashboard')
9
+ .description('查看仪表盘统计数据')
10
+ .action(async () => {
11
+ const res = await request('GET', '/dashboard');
12
+ const data = formatApiResponse(res);
13
+
14
+ const table = new Table({
15
+ head: [chalk.cyan('指标'), chalk.cyan('数值')],
16
+ });
17
+
18
+ const rows = [
19
+ ['内部项目数', data.internalProjectCount ?? '-'],
20
+ ['外部项目数', data.externalProjectCount ?? '-'],
21
+ ['预算条数', data.budgetCount ?? '-'],
22
+ ['预算总额', data.totalBudget ?? '-'],
23
+ ['已用额度', data.usedBudget ?? '-'],
24
+ ['余额', data.balance ?? '-'],
25
+ ];
26
+
27
+ for (const row of rows) {
28
+ table.push(row);
29
+ }
30
+
31
+ console.log(table.toString());
32
+ });
33
+ }
@@ -0,0 +1,21 @@
1
+ import { createResourceCommands } from './resource.js';
2
+
3
+ export function registerExternalCommand(program) {
4
+ createResourceCommands(program, 'external', '/wbxmk', {
5
+ idField: 'id',
6
+ columns: [
7
+ { field: 'id', title: 'ID' },
8
+ { field: 'xmbh', title: '项目编号' },
9
+ { field: 'xmmc', title: '项目名称' },
10
+ { field: 'xmys', title: '项目预算' },
11
+ { field: 'syys', title: '使用预算' },
12
+ { field: 'xmjd', title: '项目阶段' },
13
+ { field: 'khmc', title: '客户名称' },
14
+ ],
15
+ searchParams: [
16
+ { name: 'phase', param: 'xmjd', description: '项目阶段' },
17
+ { name: 'customerType', param: 'khlx', description: '客户类型' },
18
+ { name: 'sales', param: 'fzxs', description: '负责销售' },
19
+ ],
20
+ });
21
+ }
@@ -0,0 +1,20 @@
1
+ import { createResourceCommands } from './resource.js';
2
+
3
+ export function registerInternalCommand(program) {
4
+ createResourceCommands(program, 'internal', '/nbxmk', {
5
+ idField: 'id',
6
+ columns: [
7
+ { field: 'id', title: 'ID' },
8
+ { field: 'xmbh', title: '项目编号' },
9
+ { field: 'xmmc', title: '项目名称' },
10
+ { field: 'xmys', title: '项目预算' },
11
+ { field: 'syys', title: '使用预算' },
12
+ { field: 'xmjd', title: '项目阶段' },
13
+ { field: 'yfz', title: '研发组' },
14
+ ],
15
+ searchParams: [
16
+ { name: 'phase', param: 'xmjd', description: '项目阶段' },
17
+ { name: 'group', param: 'yfz', description: '研发组' },
18
+ ],
19
+ });
20
+ }
@@ -0,0 +1,47 @@
1
+ import { request } from '../api.js';
2
+ import { printTable, printJson, printPageInfo, extractList, formatApiResponse } from '../utils/format.js';
3
+
4
+ export function registerLogCommand(program) {
5
+ const logCmd = program.command('log').description('审计日志查询');
6
+
7
+ logCmd
8
+ .command('list')
9
+ .description('查询审计日志列表')
10
+ .option('-k, --keyword <keyword>', '关键词')
11
+ .option('-t, --table <table>', '操作的表名')
12
+ .option('-a, --action <action>', '操作类型')
13
+ .option('-p, --page <page>', '页码', '0')
14
+ .option('-s, --size <size>', '每页大小', '10')
15
+ .action(async (opts) => {
16
+ const params = new URLSearchParams();
17
+ if (opts.keyword) params.set('keyword', opts.keyword);
18
+ if (opts.table) params.set('operTable', opts.table);
19
+ if (opts.action) params.set('operAction', opts.action);
20
+ params.set('page', opts.page);
21
+ params.set('size', opts.size);
22
+
23
+ const res = await request('GET', `/oper-log?${params.toString()}`);
24
+ const data = formatApiResponse(res);
25
+ const { list, page } = extractList(data);
26
+
27
+ const columns = [
28
+ { field: 'id', title: 'ID' },
29
+ { field: 'operType', title: '操作类型' },
30
+ { field: 'operTable', title: '操作表' },
31
+ { field: 'operUser', title: '操作人' },
32
+ { field: 'operAction', title: '操作动作' },
33
+ { field: 'operTime', title: '操作时间' },
34
+ ];
35
+ const rows = list.map((item) => columns.map((col) => item[col.field] ?? ''));
36
+ printTable(columns.map((col) => col.title), rows);
37
+ printPageInfo(page);
38
+ });
39
+
40
+ logCmd
41
+ .command('get <id>')
42
+ .description('查询审计日志详情')
43
+ .action(async (id) => {
44
+ const res = await request('GET', `/oper-log/${id}`);
45
+ printJson(formatApiResponse(res));
46
+ });
47
+ }
@@ -0,0 +1,99 @@
1
+ import { request } from '../api.js';
2
+ import { printTable, printJson, printPageInfo, extractList, formatApiResponse } from '../utils/format.js';
3
+ import chalk from 'chalk';
4
+
5
+ export function createResourceCommands(program, name, endpoint, options = {}) {
6
+ const { idField = 'id', columns = [], searchParams = [] } = options;
7
+
8
+ const parentCmd = program.command(name).description(`${name} 资源管理`);
9
+
10
+ const listCmd = parentCmd
11
+ .command('list')
12
+ .description('查询列表')
13
+ .option('-k, --keyword <keyword>', '关键词')
14
+ .option('-p, --page <page>', '页码', '0')
15
+ .option('-s, --size <size>', '每页大小', '10');
16
+
17
+ for (const sp of searchParams) {
18
+ listCmd.option(`--${sp.name} <value>`, sp.description || sp.param);
19
+ }
20
+
21
+ listCmd.action(async (opts) => {
22
+ const params = new URLSearchParams();
23
+ if (opts.keyword) params.set('keyword', opts.keyword);
24
+ for (const sp of searchParams) {
25
+ if (opts[sp.name]) params.set(sp.param, opts[sp.name]);
26
+ }
27
+ params.set('page', opts.page);
28
+ params.set('size', opts.size);
29
+
30
+ const res = await request('GET', `${endpoint}?${params.toString()}`);
31
+ const data = formatApiResponse(res);
32
+ const { list, page } = extractList(data);
33
+
34
+ const rows = list.map((item) => columns.map((col) => item[col.field] ?? ''));
35
+ printTable(columns.map((col) => col.title), rows);
36
+ printPageInfo(page);
37
+ });
38
+
39
+ parentCmd
40
+ .command('get <id>')
41
+ .description('查询详情')
42
+ .action(async (id) => {
43
+ const res = await request('GET', `${endpoint}/${id}`);
44
+ printJson(formatApiResponse(res));
45
+ });
46
+
47
+ parentCmd
48
+ .command('create')
49
+ .description('新增')
50
+ .option('-d, --data <json>', 'JSON 格式数据')
51
+ .action(async (opts) => {
52
+ let body;
53
+ if (opts.data) {
54
+ body = JSON.parse(opts.data);
55
+ } else {
56
+ console.error(chalk.red('请使用 --data 传入 JSON 数据'));
57
+ process.exit(1);
58
+ }
59
+ const res = await request('POST', endpoint, { data: body });
60
+ printJson(formatApiResponse(res));
61
+ });
62
+
63
+ parentCmd
64
+ .command('update <id>')
65
+ .description('更新')
66
+ .option('-d, --data <json>', 'JSON 格式数据')
67
+ .action(async (id, opts) => {
68
+ let body;
69
+ if (opts.data) {
70
+ body = JSON.parse(opts.data);
71
+ } else {
72
+ console.error(chalk.red('请使用 --data 传入 JSON 数据'));
73
+ process.exit(1);
74
+ }
75
+ const res = await request('PUT', `${endpoint}/${id}`, { data: body });
76
+ printJson(formatApiResponse(res));
77
+ });
78
+
79
+ parentCmd
80
+ .command('delete <id>')
81
+ .description('删除')
82
+ .action(async (id) => {
83
+ const res = await request('DELETE', `${endpoint}/${id}`);
84
+ printJson(formatApiResponse(res));
85
+ });
86
+
87
+ parentCmd
88
+ .command('export')
89
+ .description('导出 Excel')
90
+ .option('-o, --output <file>', '输出文件路径')
91
+ .action(async (opts) => {
92
+ const c = (await import('../api.js')).getClient();
93
+ const res = await c.get(`${endpoint}/export`, { responseType: 'arraybuffer' });
94
+ const output = opts.output || `${name}-export.xlsx`;
95
+ const fs = await import('node:fs');
96
+ fs.writeFileSync(output, Buffer.from(res.data));
97
+ console.log(chalk.green(`已导出到: ${output}`));
98
+ });
99
+ }
@@ -0,0 +1,24 @@
1
+ import { request } from '../api.js';
2
+ import { formatApiResponse, printJson } from '../utils/format.js';
3
+ import ora from 'ora';
4
+ import chalk from 'chalk';
5
+
6
+ export function registerSyncCommand(program) {
7
+ program
8
+ .command('sync')
9
+ .description('同步低代码流程金额到预算科目')
10
+ .option('--delayed', '延迟异步执行')
11
+ .action(async (opts) => {
12
+ const url = opts.delayed ? '/sync/gsysk/flow-amounts/delayed' : '/sync/gsysk/flow-amounts';
13
+ const spinner = ora('正在同步...').start();
14
+ try {
15
+ const res = await request('POST', url);
16
+ spinner.stop();
17
+ printJson(formatApiResponse(res));
18
+ } catch (err) {
19
+ spinner.stop();
20
+ console.error(chalk.red('同步失败'));
21
+ throw err;
22
+ }
23
+ });
24
+ }
package/src/config.js ADDED
@@ -0,0 +1,59 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+
5
+ const CONFIG_DIR = path.join(os.homedir(), '.pbms');
6
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
7
+
8
+ const defaultConfig = {
9
+ baseUrl: 'http://localhost:17342/api',
10
+ apiKey: '',
11
+ };
12
+
13
+ function ensureConfigDir() {
14
+ if (!fs.existsSync(CONFIG_DIR)) {
15
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
16
+ }
17
+ }
18
+
19
+ export function loadConfig() {
20
+ ensureConfigDir();
21
+ if (!fs.existsSync(CONFIG_FILE)) {
22
+ return { ...defaultConfig };
23
+ }
24
+ try {
25
+ const raw = fs.readFileSync(CONFIG_FILE, 'utf8');
26
+ const parsed = JSON.parse(raw);
27
+ return { ...defaultConfig, ...parsed };
28
+ } catch (err) {
29
+ console.error('读取配置文件失败:', err.message);
30
+ return { ...defaultConfig };
31
+ }
32
+ }
33
+
34
+ export function saveConfig(config) {
35
+ ensureConfigDir();
36
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
37
+ }
38
+
39
+ export function setConfigValue(key, value) {
40
+ const config = loadConfig();
41
+ if (!Object.prototype.hasOwnProperty.call(defaultConfig, key)) {
42
+ throw new Error(`未知配置项: ${key}。有效项: ${Object.keys(defaultConfig).join(', ')}`);
43
+ }
44
+ config[key] = value;
45
+ saveConfig(config);
46
+ return config;
47
+ }
48
+
49
+ export function getConfigValue(key) {
50
+ const config = loadConfig();
51
+ if (key) {
52
+ return config[key];
53
+ }
54
+ return config;
55
+ }
56
+
57
+ export function getConfigPath() {
58
+ return CONFIG_FILE;
59
+ }
@@ -0,0 +1,61 @@
1
+ import Table from 'cli-table3';
2
+ import chalk from 'chalk';
3
+
4
+ export function printJson(data) {
5
+ console.log(JSON.stringify(data, null, 2));
6
+ }
7
+
8
+ export function printTable(headers, rows) {
9
+ if (!rows || rows.length === 0) {
10
+ console.log(chalk.yellow('暂无数据'));
11
+ return;
12
+ }
13
+
14
+ const table = new Table({
15
+ head: headers.map((h) => chalk.cyan(h)),
16
+ wordWrap: true,
17
+ truncate: '...',
18
+ });
19
+
20
+ for (const row of rows) {
21
+ table.push(row);
22
+ }
23
+
24
+ console.log(table.toString());
25
+ }
26
+
27
+ export function printPageInfo(page) {
28
+ if (!page) return;
29
+ const { number, size, totalElements, totalPages } = page;
30
+ console.log(
31
+ chalk.gray(
32
+ `第 ${number + 1} 页 / 共 ${totalPages} 页,每页 ${size} 条,总计 ${totalElements} 条`
33
+ )
34
+ );
35
+ }
36
+
37
+ export function formatApiResponse(data) {
38
+ if (data && typeof data === 'object' && 'code' in data) {
39
+ if (data.code !== 200) {
40
+ throw new Error(data.message || '接口返回错误');
41
+ }
42
+ return data.data;
43
+ }
44
+ return data;
45
+ }
46
+
47
+ export function extractList(response) {
48
+ const data = formatApiResponse(response);
49
+ if (data && Array.isArray(data.content)) {
50
+ return {
51
+ list: data.content,
52
+ page: {
53
+ number: data.number ?? 0,
54
+ size: data.size ?? 0,
55
+ totalElements: data.totalElements ?? 0,
56
+ totalPages: data.totalPages ?? 0,
57
+ },
58
+ };
59
+ }
60
+ return { list: Array.isArray(data) ? data : [], page: null };
61
+ }
@@ -0,0 +1,19 @@
1
+ import { input, confirm } from '@inquirer/prompts';
2
+
3
+ export async function promptFor(fields) {
4
+ const result = {};
5
+ for (const field of fields) {
6
+ const value = await input({
7
+ message: field.message,
8
+ default: field.default || '',
9
+ });
10
+ if (value) {
11
+ result[field.name] = value;
12
+ }
13
+ }
14
+ return result;
15
+ }
16
+
17
+ export async function confirmAction(message) {
18
+ return confirm({ message, default: false });
19
+ }