@newpeak/barista-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/.eslintrc.json +23 -0
- package/.prettierrc +9 -0
- package/.sisyphus/notepads/liberica-employees/learnings.md +73 -0
- package/AGENTS.md +270 -0
- package/CONTRIBUTING.md +291 -0
- package/README.md +707 -0
- package/bin/barista +6 -0
- package/bin/barista.js +3 -0
- package/docs/ARCHITECTURE.md +184 -0
- package/docs/COMMANDS.md +352 -0
- package/docs/COMMAND_DESIGN_SPEC.md +811 -0
- package/docs/INTEGRATION_NOTES.md +270 -0
- package/docs/commands/REFERENCE.md +297 -0
- package/docs/commands/arabica/auth/index.md +296 -0
- package/docs/commands/liberica/auth/index.md +133 -0
- package/docs/commands/liberica/context/index.md +60 -0
- package/docs/commands/liberica/employees/create.md +185 -0
- package/docs/commands/liberica/employees/disable.md +138 -0
- package/docs/commands/liberica/employees/enable.md +137 -0
- package/docs/commands/liberica/employees/get.md +153 -0
- package/docs/commands/liberica/employees/list.md +168 -0
- package/docs/commands/liberica/employees/update.md +180 -0
- package/docs/commands/liberica/orgs/list.md +62 -0
- package/docs/commands/liberica/positions/list.md +61 -0
- package/docs/commands/liberica/roles/list.md +67 -0
- package/docs/commands/liberica/users/create.md +170 -0
- package/docs/commands/liberica/users/get.md +151 -0
- package/docs/commands/liberica/users/list.md +175 -0
- package/package.json +37 -0
- package/src/commands/arabica/auth/index.ts +277 -0
- package/src/commands/arabica/auth/login.ts +5 -0
- package/src/commands/arabica/auth/logout.ts +5 -0
- package/src/commands/arabica/auth/register.ts +5 -0
- package/src/commands/arabica/auth/status.ts +5 -0
- package/src/commands/arabica/index.ts +23 -0
- package/src/commands/auth.ts +107 -0
- package/src/commands/context.ts +60 -0
- package/src/commands/liberica/auth/index.ts +170 -0
- package/src/commands/liberica/context/index.ts +43 -0
- package/src/commands/liberica/employees/create.ts +275 -0
- package/src/commands/liberica/employees/delete.ts +122 -0
- package/src/commands/liberica/employees/disable.ts +97 -0
- package/src/commands/liberica/employees/enable.ts +97 -0
- package/src/commands/liberica/employees/get.ts +115 -0
- package/src/commands/liberica/employees/index.ts +23 -0
- package/src/commands/liberica/employees/list.ts +131 -0
- package/src/commands/liberica/employees/update.ts +157 -0
- package/src/commands/liberica/index.ts +36 -0
- package/src/commands/liberica/orgs/index.ts +35 -0
- package/src/commands/liberica/positions/index.ts +30 -0
- package/src/commands/liberica/roles/index.ts +59 -0
- package/src/commands/liberica/users/create.ts +132 -0
- package/src/commands/liberica/users/delete.ts +49 -0
- package/src/commands/liberica/users/disable.ts +41 -0
- package/src/commands/liberica/users/enable.ts +30 -0
- package/src/commands/liberica/users/get.ts +46 -0
- package/src/commands/liberica/users/index.ts +27 -0
- package/src/commands/liberica/users/list.ts +68 -0
- package/src/commands/liberica/users/me.ts +42 -0
- package/src/commands/liberica/users/reset-password.ts +42 -0
- package/src/commands/liberica/users/update.ts +48 -0
- package/src/core/api/client.ts +825 -0
- package/src/core/auth/token-manager.ts +183 -0
- package/src/core/config/manager.ts +164 -0
- package/src/index.ts +37 -0
- package/src/types/employee.ts +102 -0
- package/src/types/index.ts +75 -0
- package/src/types/org.ts +25 -0
- package/src/types/position.ts +24 -0
- package/src/types/user.ts +64 -0
- package/tests/unit/commands/arabica/auth.test.ts +230 -0
- package/tests/unit/commands/liberica/auth.test.ts +175 -0
- package/tests/unit/commands/liberica/context.test.ts +98 -0
- package/tests/unit/commands/liberica/employees/create.test.ts +463 -0
- package/tests/unit/commands/liberica/employees/disable.test.ts +82 -0
- package/tests/unit/commands/liberica/employees/enable.test.ts +82 -0
- package/tests/unit/commands/liberica/employees/get.test.ts +111 -0
- package/tests/unit/commands/liberica/employees/list.test.ts +294 -0
- package/tests/unit/commands/liberica/employees/update.test.ts +210 -0
- package/tests/unit/config.test.ts +141 -0
- package/tests/unit/types.test.ts +195 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# barista liberica users list
|
|
2
|
+
|
|
3
|
+
分页查询用户列表。
|
|
4
|
+
|
|
5
|
+
## 2.1 命令元数据
|
|
6
|
+
|
|
7
|
+
| 字段 | 值 |
|
|
8
|
+
|------|-----|
|
|
9
|
+
| 完整命令 | `barista liberica users list` |
|
|
10
|
+
| 功能描述 | 分页查询用户列表 |
|
|
11
|
+
| HTTP方法 | GET |
|
|
12
|
+
| 是否需要认证 | ✅ 是 |
|
|
13
|
+
| 是否支持dry-run | ⬜ 否 |
|
|
14
|
+
|
|
15
|
+
## 2.2 后端接口引用
|
|
16
|
+
|
|
17
|
+
### Controller位置
|
|
18
|
+
```
|
|
19
|
+
coffee-liberica-end/
|
|
20
|
+
└── facade/liberica-facade-enterprise/
|
|
21
|
+
└── src/main/java/com/newpeak/liberica/facade/enterprise/controller/
|
|
22
|
+
└── EnterpriseSysUserController.java
|
|
23
|
+
└── page(@GetResource(name = "分页查询-用户信息", path = "/page"))
|
|
24
|
+
└── public ResponseData<PageResult<SysUserDto>> page(
|
|
25
|
+
ErpSysUserRequest erpSysUserRequest
|
|
26
|
+
)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Request DTO位置
|
|
30
|
+
```
|
|
31
|
+
coffee-liberica-end/
|
|
32
|
+
└── business/liberica-business-auth/
|
|
33
|
+
└── src/main/java/com/newpeak/liberica/business/auth/pojo/request/
|
|
34
|
+
└── ErpSysUserRequest.java
|
|
35
|
+
├── userId: Long (@NotNull on edit/delete/detail/updateStatus/resetPassword)
|
|
36
|
+
├── realName: String (@NotBlank on add/edit/updateInfo)
|
|
37
|
+
├── account: String (@NotBlank on add/edit, unique validation)
|
|
38
|
+
├── newPassword: String (@NotBlank on updatePwd)
|
|
39
|
+
├── avatar: Long (@NotNull on updateAvatar)
|
|
40
|
+
├── email: String (@Email validation)
|
|
41
|
+
├── roleIdList: Set<Long> (@NotEmpty on add/edit)
|
|
42
|
+
├── userIdList: Set<Long> (@NotEmpty on batchDelete)
|
|
43
|
+
└── employeeId: Long
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Response DTO位置
|
|
47
|
+
```
|
|
48
|
+
coffee-liberica-end/
|
|
49
|
+
└── business/liberica-business-auth/
|
|
50
|
+
└── src/main/java/com/newpeak/liberica/business/auth/pojo/
|
|
51
|
+
└── SysUserDto.java
|
|
52
|
+
├── userId: Long
|
|
53
|
+
├── realName: String
|
|
54
|
+
├── account: String
|
|
55
|
+
├── email: String
|
|
56
|
+
├── employeeId: Long
|
|
57
|
+
├── employeeNumber: String
|
|
58
|
+
├── employeeName: String
|
|
59
|
+
├── roleNameList: List<String>
|
|
60
|
+
├── statusFlag: Integer (1=enable, 2=disable)
|
|
61
|
+
├── createTime: Date
|
|
62
|
+
└── readOnly: Boolean
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 分页包装器
|
|
66
|
+
```
|
|
67
|
+
cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult
|
|
68
|
+
├── total: long
|
|
69
|
+
├── size: long
|
|
70
|
+
├── current: long
|
|
71
|
+
└── records: List<SysUserDto>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 2.3 CLI参数设计
|
|
75
|
+
|
|
76
|
+
### 命令结构
|
|
77
|
+
```
|
|
78
|
+
barista liberica users list [options]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 全局选项
|
|
82
|
+
| 选项 | 类型 | 说明 |
|
|
83
|
+
|------|------|------|
|
|
84
|
+
| `--env` | string | 目标环境(dev\|test\|prod-cn\|prod-jp) |
|
|
85
|
+
| `--tenant` | string | 租户代码 |
|
|
86
|
+
| `--json` | boolean | JSON输出 |
|
|
87
|
+
|
|
88
|
+
### 命令选项
|
|
89
|
+
| 选项 | 短选项 | 类型 | 必填 | 默认值 | 说明 | 对应DTO字段 |
|
|
90
|
+
|------|--------|------|------|--------|------|-------------|
|
|
91
|
+
| --page | -p | number | ⬜ | 1 | 页码 | pageNo (0-based in API) |
|
|
92
|
+
| --size | -s | number | ⬜ | 20 | 每页条数 | pageSize |
|
|
93
|
+
| --account | -a | string | ⬜ | - | 账号模糊查询 | account |
|
|
94
|
+
| --name | -n | string | ⬜ | - | 姓名模糊查询 | realName |
|
|
95
|
+
| --status | — | string | ⬜ | - | 状态(enable/disable) | statusFlag |
|
|
96
|
+
|
|
97
|
+
## 2.4 字段映射表
|
|
98
|
+
|
|
99
|
+
| CLI参数 | DTO字段/Query参数 | 类型转换 | 验证规则 |
|
|
100
|
+
|---------|---------|----------|----------|
|
|
101
|
+
| --page / -p | pageNo | number→Long, CLI page 1 → API page 0 | >= 1 |
|
|
102
|
+
| --size / -s | pageSize | number→Long | 1-100 |
|
|
103
|
+
| --account / -a | account | 直接传递 | 模糊查询 |
|
|
104
|
+
| --name / -n | realName | 直接传递 | 模糊查询 |
|
|
105
|
+
| --status | statusFlag | string→Integer | enable=1, disable=2 |
|
|
106
|
+
|
|
107
|
+
## 2.5 错误码引用
|
|
108
|
+
|
|
109
|
+
### ExceptionEnum位置
|
|
110
|
+
```
|
|
111
|
+
coffee-liberica-end/
|
|
112
|
+
└── business/liberica-business-auth/
|
|
113
|
+
└── src/main/java/com/newpeak/liberica/business/auth/enums/exception/
|
|
114
|
+
└── ErpUserExceptionEnum.java
|
|
115
|
+
├── UPDATE_SELF_PSW_FORBIDDEN("010011510001", "不能重置自己的密码")
|
|
116
|
+
├── DEL_SELF_FORBIDDEN("010011510002", "不能删除自己的账号")
|
|
117
|
+
├── UPDATE_ADMIN_FORBIDDEN("010011510003", "不能更新管理员账号")
|
|
118
|
+
├── UPDATE_SELF_ACCOUNT("010011510004", "不能更新自己的账号")
|
|
119
|
+
├── WRONG_STATUS_FLAG("010011510005", "错误的statusFlag")
|
|
120
|
+
├── WRONG_ACCOUNT("010011510006", "错误的账号")
|
|
121
|
+
├── TOKEN_EXPIRED("010011510007", "链接已过期")
|
|
122
|
+
├── EMAIL_INCONSISTENT("010011510008", "账号不一致")
|
|
123
|
+
├── ACCOUNT_DUPLICATED("010011510009", "已存在相同账号")
|
|
124
|
+
├── SEND_EMAIL_FAILED("010011510010", "发送邮件失败")
|
|
125
|
+
├── BOUND_USER_DEL_FORBID("010011510011", "已绑定用户职员不能被删除")
|
|
126
|
+
└── ERP_NOT_EXIST("010011510012", "对应erp不存在")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 已知错误码
|
|
130
|
+
| 错误码 | 错误消息 | 触发条件 |
|
|
131
|
+
|--------|----------|----------|
|
|
132
|
+
| 010011510005 | 错误的statusFlag | 状态参数无效 |
|
|
133
|
+
|
|
134
|
+
## 2.6 权限检查
|
|
135
|
+
|
|
136
|
+
| 检查项 | 位置 | 说明 |
|
|
137
|
+
|--------|------|------|
|
|
138
|
+
| 注解 | `@GetResource(path = "/page")` | 无 requiredPermission(无需权限码) |
|
|
139
|
+
|
|
140
|
+
**注意**:page 接口在 Controller 层无需权限校验,数据范围通过 tenantId 隔离。
|
|
141
|
+
|
|
142
|
+
## 2.7 实现要点
|
|
143
|
+
|
|
144
|
+
1. **分页默认值**:page=1, size=20
|
|
145
|
+
2. **分页转换**:CLI页码从1开始,API页码从0开始(CLI page 1 → API page 0)
|
|
146
|
+
3. **模糊查询**:account 和 realName 支持模糊查询
|
|
147
|
+
4. **状态映射**:statusFlag 1=启用(enable), 2=禁用(disable)
|
|
148
|
+
5. **无结果处理**:返回空列表,不抛出异常
|
|
149
|
+
6. **tenantId来源**:来自JWT token
|
|
150
|
+
7. **输出格式**:表格形式展示,分页信息在底部显示
|
|
151
|
+
|
|
152
|
+
## 2.8 示例用法
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# 默认分页
|
|
156
|
+
barista liberica users list
|
|
157
|
+
|
|
158
|
+
# 指定页码和每页条数
|
|
159
|
+
barista liberica users list --page 2 --size 50
|
|
160
|
+
|
|
161
|
+
# 按账号模糊搜索
|
|
162
|
+
barista liberica users list --account "zhang"
|
|
163
|
+
|
|
164
|
+
# 按姓名模糊搜索
|
|
165
|
+
barista liberica users list --name "张三"
|
|
166
|
+
|
|
167
|
+
# 按状态筛选
|
|
168
|
+
barista liberica users list --status enable
|
|
169
|
+
|
|
170
|
+
# 组合筛选
|
|
171
|
+
barista liberica users list --page 1 --size 10 --status enable --name "张"
|
|
172
|
+
|
|
173
|
+
# JSON 输出(便于脚本处理)
|
|
174
|
+
barista liberica users list --page 1 --size 20 --json
|
|
175
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@newpeak/barista-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI Tools CLI for Liberica and Arabica services",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"barista": "./bin/barista.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsc --watch",
|
|
12
|
+
"start": "node ./bin/barista.js",
|
|
13
|
+
"test": "vitest",
|
|
14
|
+
"test:unit": "vitest run",
|
|
15
|
+
"lint": "eslint src --ext .ts,.js",
|
|
16
|
+
"format": "prettier --write src"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"commander": "^12.0.0",
|
|
20
|
+
"axios": "^1.6.8",
|
|
21
|
+
"keytar": "^7.9.0",
|
|
22
|
+
"yaml": "^2.4.0",
|
|
23
|
+
"chalk": "^5.3.0",
|
|
24
|
+
"inquirer": "^9.2.12",
|
|
25
|
+
"cli-table3": "^0.6.3"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"typescript": "^5.4.5",
|
|
29
|
+
"@types/node": "^20.0.0",
|
|
30
|
+
"@types/inquirer": "^9.0.7",
|
|
31
|
+
"vitest": "^1.5.0",
|
|
32
|
+
"eslint": "^8.57.0",
|
|
33
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
34
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
35
|
+
"prettier": "^3.2.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { configManager } from '../../../core/config/manager.js';
|
|
5
|
+
import { tokenManager } from '../../../core/auth/token-manager.js';
|
|
6
|
+
import { apiClient } from '../../../core/api/client.js';
|
|
7
|
+
import { Environment } from '../../../types/index.js';
|
|
8
|
+
|
|
9
|
+
export function createArabicaAuthCommand(): Command {
|
|
10
|
+
const authCommand = new Command('auth');
|
|
11
|
+
authCommand.description('Manage Arabica authentication');
|
|
12
|
+
|
|
13
|
+
authCommand.action(async () => {
|
|
14
|
+
await authCommand.help();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
authCommand
|
|
18
|
+
.command('login')
|
|
19
|
+
.description('Login to Arabica')
|
|
20
|
+
.arguments('<env> [account] [password]')
|
|
21
|
+
.option('--env <environment>', 'Environment (dev|test|prod-cn|prod-jp)')
|
|
22
|
+
.option('--account <account>', 'Account (username or email)')
|
|
23
|
+
.option('--password <password>', 'Password')
|
|
24
|
+
.option('--remember', 'Remember login status')
|
|
25
|
+
.action(async (env: string, account: string, password: string, options: Record<string, any>) => {
|
|
26
|
+
const context = configManager.getCurrentContext();
|
|
27
|
+
const environment = (options.env as Environment) || env || context.environment;
|
|
28
|
+
const rememberMe = options.remember || false;
|
|
29
|
+
|
|
30
|
+
console.log(chalk.bold(`\n🔐 Login to Arabica (${environment})\n`));
|
|
31
|
+
|
|
32
|
+
let resolvedAccount = options.account || account;
|
|
33
|
+
let resolvedPassword = options.password || password;
|
|
34
|
+
|
|
35
|
+
if (!resolvedAccount) {
|
|
36
|
+
const { account: inputAccount } = await inquirer.prompt([
|
|
37
|
+
{
|
|
38
|
+
type: 'input',
|
|
39
|
+
name: 'account',
|
|
40
|
+
message: 'Enter account (username or email):',
|
|
41
|
+
validate: (input: string) => input.length > 0 || 'Account is required',
|
|
42
|
+
},
|
|
43
|
+
]);
|
|
44
|
+
resolvedAccount = inputAccount;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!resolvedPassword) {
|
|
48
|
+
const { password: inputPassword } = await inquirer.prompt([
|
|
49
|
+
{
|
|
50
|
+
type: 'password',
|
|
51
|
+
name: 'password',
|
|
52
|
+
message: 'Enter password:',
|
|
53
|
+
validate: (input: string) => input.length > 0 || 'Password is required',
|
|
54
|
+
},
|
|
55
|
+
]);
|
|
56
|
+
resolvedPassword = inputPassword;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const response = await apiClient.loginArabica(environment, resolvedAccount, resolvedPassword, rememberMe);
|
|
61
|
+
|
|
62
|
+
if (response.success && response.data) {
|
|
63
|
+
try {
|
|
64
|
+
await tokenManager.setToken(
|
|
65
|
+
{
|
|
66
|
+
service: 'arabica',
|
|
67
|
+
environment,
|
|
68
|
+
},
|
|
69
|
+
response.data.token
|
|
70
|
+
);
|
|
71
|
+
} catch (keytarError) {
|
|
72
|
+
console.error(chalk.red(`\n✗ Failed to save token to keychain: ${keytarError instanceof Error ? keytarError.message : 'Unknown error'}`));
|
|
73
|
+
console.error(chalk.gray(' This may occur in headless environments without D-Bus/X11.'));
|
|
74
|
+
console.error(chalk.gray(' The login was successful but the token could not be persisted.\n'));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log(chalk.green('\n✓ Login successful'));
|
|
79
|
+
if (response.data.expiresAt) {
|
|
80
|
+
console.log(chalk.gray(` Token expires: ${response.data.expiresAt}`));
|
|
81
|
+
}
|
|
82
|
+
console.log();
|
|
83
|
+
} else {
|
|
84
|
+
console.error(chalk.red(`\n✗ Login failed: ${response.error?.message || 'Unknown error'}`));
|
|
85
|
+
if (response.error?.code) {
|
|
86
|
+
console.error(chalk.gray(` Error code: ${response.error.code}`));
|
|
87
|
+
}
|
|
88
|
+
console.error();
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error(chalk.red(`\n✗ Login error: ${error instanceof Error ? error.message : 'Unknown error'}\n`));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
authCommand
|
|
98
|
+
.command('register')
|
|
99
|
+
.description('Register new Arabica account')
|
|
100
|
+
.arguments('[env] [email] [password] [phone] [username]')
|
|
101
|
+
.option('--env <environment>', 'Environment (dev|test|prod-cn|prod-jp)')
|
|
102
|
+
.option('--email <email>', 'Email address')
|
|
103
|
+
.option('--password <password>', 'Password')
|
|
104
|
+
.option('--phone <phone>', 'Phone number')
|
|
105
|
+
.option('--username <username>', 'User name')
|
|
106
|
+
.option('--policy <policy>', 'Policy agreement (e.g., Y)')
|
|
107
|
+
.option('--privacy <privacy>', 'Privacy agreement (e.g., Y)')
|
|
108
|
+
.action(async (env: string, email: string, password: string, phone: string, username: string, options: Record<string, any>) => {
|
|
109
|
+
const context = configManager.getCurrentContext();
|
|
110
|
+
const environment = (options.env as Environment) || env || context.environment;
|
|
111
|
+
|
|
112
|
+
console.log(chalk.bold(`\n📝 Register to Arabica (${environment})\n`));
|
|
113
|
+
|
|
114
|
+
let resolvedEmail = options.email || email;
|
|
115
|
+
let resolvedPassword = options.password || password;
|
|
116
|
+
let resolvedPhone = options.phone || phone;
|
|
117
|
+
let resolvedUsername = options.username || username;
|
|
118
|
+
let resolvedPolicy = options.policy;
|
|
119
|
+
let resolvedPrivacy = options.privacy;
|
|
120
|
+
|
|
121
|
+
if (!resolvedEmail) {
|
|
122
|
+
const { email: inputEmail } = await inquirer.prompt([
|
|
123
|
+
{
|
|
124
|
+
type: 'input',
|
|
125
|
+
name: 'email',
|
|
126
|
+
message: 'Enter email address:',
|
|
127
|
+
validate: (input: string) => input.length > 0 || 'Email is required',
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
resolvedEmail = inputEmail;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!resolvedPassword) {
|
|
134
|
+
const { password: inputPassword } = await inquirer.prompt([
|
|
135
|
+
{
|
|
136
|
+
type: 'password',
|
|
137
|
+
name: 'password',
|
|
138
|
+
message: 'Enter password:',
|
|
139
|
+
validate: (input: string) => input.length > 0 || 'Password is required',
|
|
140
|
+
},
|
|
141
|
+
]);
|
|
142
|
+
resolvedPassword = inputPassword;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!resolvedPhone) {
|
|
146
|
+
const { phone: inputPhone } = await inquirer.prompt([
|
|
147
|
+
{
|
|
148
|
+
type: 'input',
|
|
149
|
+
name: 'phone',
|
|
150
|
+
message: 'Enter phone number:',
|
|
151
|
+
validate: (input: string) => input.length > 0 || 'Phone is required',
|
|
152
|
+
},
|
|
153
|
+
]);
|
|
154
|
+
resolvedPhone = inputPhone;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!resolvedUsername) {
|
|
158
|
+
const { username: inputUsername } = await inquirer.prompt([
|
|
159
|
+
{
|
|
160
|
+
type: 'input',
|
|
161
|
+
name: 'username',
|
|
162
|
+
message: 'Enter user name:',
|
|
163
|
+
validate: (input: string) => input.length > 0 || 'User name is required',
|
|
164
|
+
},
|
|
165
|
+
]);
|
|
166
|
+
resolvedUsername = inputUsername;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!resolvedPolicy) {
|
|
170
|
+
const { policy: inputPolicy } = await inquirer.prompt([
|
|
171
|
+
{
|
|
172
|
+
type: 'input',
|
|
173
|
+
name: 'policy',
|
|
174
|
+
message: 'Policy agreement (press Enter for Y):',
|
|
175
|
+
default: 'Y',
|
|
176
|
+
},
|
|
177
|
+
]);
|
|
178
|
+
resolvedPolicy = inputPolicy || 'Y';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!resolvedPrivacy) {
|
|
182
|
+
const { privacy: inputPrivacy } = await inquirer.prompt([
|
|
183
|
+
{
|
|
184
|
+
type: 'input',
|
|
185
|
+
name: 'privacy',
|
|
186
|
+
message: 'Privacy agreement (press Enter for Y):',
|
|
187
|
+
default: 'Y',
|
|
188
|
+
},
|
|
189
|
+
]);
|
|
190
|
+
resolvedPrivacy = inputPrivacy || 'Y';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const response = await apiClient.registerArabica(
|
|
195
|
+
environment,
|
|
196
|
+
resolvedEmail,
|
|
197
|
+
resolvedEmail,
|
|
198
|
+
resolvedPassword,
|
|
199
|
+
resolvedPhone,
|
|
200
|
+
resolvedUsername,
|
|
201
|
+
resolvedPolicy,
|
|
202
|
+
resolvedPrivacy
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
if (response.success && response.data) {
|
|
206
|
+
console.log(chalk.green('\n✓ Registration successful'));
|
|
207
|
+
if (response.data.userId) {
|
|
208
|
+
console.log(chalk.gray(` User ID: ${response.data.userId}`));
|
|
209
|
+
}
|
|
210
|
+
console.log();
|
|
211
|
+
} else {
|
|
212
|
+
console.error(chalk.red(`\n✗ Registration failed: ${response.error?.message || 'Unknown error'}`));
|
|
213
|
+
if (response.error?.code) {
|
|
214
|
+
console.error(chalk.gray(` Error code: ${response.error.code}`));
|
|
215
|
+
}
|
|
216
|
+
console.error();
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
console.error(chalk.red(`\n✗ Registration error: ${error instanceof Error ? error.message : 'Unknown error'}\n`));
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
authCommand
|
|
226
|
+
.command('status')
|
|
227
|
+
.description('Check Arabica authentication status')
|
|
228
|
+
.action(async () => {
|
|
229
|
+
const environments: Environment[] = ['dev', 'test', 'prod-cn', 'prod-jp'];
|
|
230
|
+
|
|
231
|
+
console.log(chalk.bold('\n🔐 Arabica Authentication Status\n'));
|
|
232
|
+
|
|
233
|
+
for (const env of environments) {
|
|
234
|
+
const token = await tokenManager.getToken({ service: 'arabica', environment: env });
|
|
235
|
+
const statusIcon = token ? chalk.green('✓') : chalk.red('✗');
|
|
236
|
+
const statusText = token ? 'Logged in' : 'Not logged in';
|
|
237
|
+
console.log(` ${statusIcon} ${chalk.gray('arabica')} (${env}): ${statusText}`);
|
|
238
|
+
}
|
|
239
|
+
console.log();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
authCommand
|
|
243
|
+
.command('logout')
|
|
244
|
+
.description('Logout from Arabica and clear token')
|
|
245
|
+
.option('--env <environment>', 'Environment (dev|test|prod-cn|prod-jp)')
|
|
246
|
+
.option('--all', 'Clear all Arabica tokens')
|
|
247
|
+
.action(async (options) => {
|
|
248
|
+
if (options.all) {
|
|
249
|
+
const allTokens = await tokenManager.findAllTokens();
|
|
250
|
+
for (const cred of allTokens) {
|
|
251
|
+
const parts = cred.account.split(':');
|
|
252
|
+
if (parts[0] === 'arabica') {
|
|
253
|
+
await tokenManager.deleteToken({
|
|
254
|
+
service: 'arabica',
|
|
255
|
+
environment: parts[1] as Environment,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
console.log(chalk.green('\n✓ All Arabica tokens cleared\n'));
|
|
260
|
+
} else {
|
|
261
|
+
const environment = (options.env as Environment) || configManager.getCurrentContext().environment;
|
|
262
|
+
|
|
263
|
+
const deleted = await tokenManager.deleteToken({
|
|
264
|
+
service: 'arabica',
|
|
265
|
+
environment,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
if (deleted) {
|
|
269
|
+
console.log(chalk.green(`\n✓ Token cleared for Arabica (${environment})\n`));
|
|
270
|
+
} else {
|
|
271
|
+
console.log(chalk.gray(`\nNo token found for Arabica (${environment})\n`));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
return authCommand;
|
|
277
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { createArabicaAuthCommand } from './auth/index.js';
|
|
4
|
+
|
|
5
|
+
export function createArabicaCommand(): Command {
|
|
6
|
+
const arabicaCommand = new Command('arabica');
|
|
7
|
+
arabicaCommand.description('Arabica SaaS platform operations');
|
|
8
|
+
|
|
9
|
+
arabicaCommand.addCommand(createArabicaAuthCommand());
|
|
10
|
+
|
|
11
|
+
arabicaCommand.action(() => {
|
|
12
|
+
console.log(chalk.bold('\n🛒 Arabica Commands\n'));
|
|
13
|
+
console.log(' Use "barista arabica auth <command>" for authentication');
|
|
14
|
+
console.log(' Use "barista arabica members <command>" for member operations');
|
|
15
|
+
console.log(' Use "barista arabica enterprises <command>" for enterprise operations');
|
|
16
|
+
console.log(' Use "barista arabica products <command>" for product operations');
|
|
17
|
+
console.log(' Use "barista arabica subscriptions <command>" for subscription operations');
|
|
18
|
+
console.log(' Use "barista arabica access <command>" for access management');
|
|
19
|
+
console.log('\n Run "barista arabica <command> --help" for more details\n');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return arabicaCommand;
|
|
23
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { configManager } from '../core/config/manager.js';
|
|
5
|
+
import { tokenManager } from '../core/auth/token-manager.js';
|
|
6
|
+
import { Service } from '../types/index.js';
|
|
7
|
+
|
|
8
|
+
export function createAuthCommand(): Command {
|
|
9
|
+
const authCommand = new Command('auth');
|
|
10
|
+
authCommand.description('Manage authentication tokens');
|
|
11
|
+
|
|
12
|
+
authCommand.action(async () => {
|
|
13
|
+
await authCommand.help();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
authCommand
|
|
17
|
+
.command('login')
|
|
18
|
+
.description('Login with token')
|
|
19
|
+
.option('--service <service>', 'Service (liberica|arabica)')
|
|
20
|
+
.option('--env <environment>', 'Environment (dev|test|prod-cn|prod-jp)')
|
|
21
|
+
.action(async (options) => {
|
|
22
|
+
const context = configManager.getCurrentContext();
|
|
23
|
+
const service = (options.service as Service) || context.service;
|
|
24
|
+
const environment = options.env || context.environment;
|
|
25
|
+
|
|
26
|
+
console.log(chalk.bold(`\n🔐 Login to ${service} (${environment})\n`));
|
|
27
|
+
|
|
28
|
+
const answers = await inquirer.prompt([
|
|
29
|
+
{
|
|
30
|
+
type: 'password',
|
|
31
|
+
name: 'token',
|
|
32
|
+
message: 'Enter your token:',
|
|
33
|
+
validate: (input: string) => input.length > 0 || 'Token is required',
|
|
34
|
+
},
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
await tokenManager.setToken(
|
|
38
|
+
{
|
|
39
|
+
service,
|
|
40
|
+
environment: environment as 'dev' | 'test' | 'prod-cn' | 'prod-jp',
|
|
41
|
+
tenant: context.tenant,
|
|
42
|
+
},
|
|
43
|
+
answers.token
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
console.log(chalk.green('\n✓ Token saved successfully\n'));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
authCommand
|
|
50
|
+
.command('status')
|
|
51
|
+
.description('Check authentication status')
|
|
52
|
+
.action(async () => {
|
|
53
|
+
const services: Service[] = ['liberica', 'arabica'];
|
|
54
|
+
const environments = ['dev', 'test', 'prod-cn', 'prod-jp'] as const;
|
|
55
|
+
|
|
56
|
+
console.log(chalk.bold('\n🔐 Authentication Status\n'));
|
|
57
|
+
|
|
58
|
+
for (const service of services) {
|
|
59
|
+
for (const env of environments) {
|
|
60
|
+
const token = await tokenManager.getToken({ service, environment: env });
|
|
61
|
+
const statusIcon = token ? chalk.green('✓') : chalk.red('✗');
|
|
62
|
+
const statusText = token ? 'Logged in' : 'Not logged in';
|
|
63
|
+
console.log(` ${statusIcon} ${chalk.gray(service)} (${env}): ${statusText}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
console.log();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
authCommand
|
|
70
|
+
.command('logout')
|
|
71
|
+
.description('Logout and clear token')
|
|
72
|
+
.option('--service <service>', 'Service (liberica|arabica)')
|
|
73
|
+
.option('--env <environment>', 'Environment (dev|test|prod-cn|prod-jp)')
|
|
74
|
+
.option('--all', 'Clear all tokens')
|
|
75
|
+
.action(async (options) => {
|
|
76
|
+
if (options.all) {
|
|
77
|
+
const allTokens = await tokenManager.findAllTokens();
|
|
78
|
+
for (const cred of allTokens) {
|
|
79
|
+
const parts = cred.account.split(':');
|
|
80
|
+
await tokenManager.deleteToken({
|
|
81
|
+
service: parts[0] as Service,
|
|
82
|
+
environment: parts[1] as 'dev' | 'test' | 'prod-cn' | 'prod-jp',
|
|
83
|
+
tenant: parts[2],
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
console.log(chalk.green('\n✓ All tokens cleared\n'));
|
|
87
|
+
} else {
|
|
88
|
+
const context = configManager.getCurrentContext();
|
|
89
|
+
const service = (options.service as Service) || context.service;
|
|
90
|
+
const environment = options.env || context.environment;
|
|
91
|
+
|
|
92
|
+
const deleted = await tokenManager.deleteToken({
|
|
93
|
+
service,
|
|
94
|
+
environment: environment as 'dev' | 'test' | 'prod-cn' | 'prod-jp',
|
|
95
|
+
tenant: context.tenant,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (deleted) {
|
|
99
|
+
console.log(chalk.green(`\n✓ Token cleared for ${service} (${environment})\n`));
|
|
100
|
+
} else {
|
|
101
|
+
console.log(chalk.gray(`\nNo token found for ${service} (${environment})\n`));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return authCommand;
|
|
107
|
+
}
|