@dreamor/atlas-cli 0.7.23 → 0.7.25
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 +1 -59
- package/atlas.cjs +226 -0
- package/package.json +7 -9
- package/atlas.js +0 -5
- package/dist/adapters/atlas/auth/browser.js +0 -37
- package/dist/adapters/atlas/auth/index.js +0 -4
- package/dist/adapters/atlas/auth/login.js +0 -187
- package/dist/adapters/atlas/auth/portable.js +0 -193
- package/dist/adapters/atlas/auth/refresh.js +0 -138
- package/dist/adapters/atlas/auth/session.js +0 -167
- package/dist/adapters/atlas/cli.js +0 -561
- package/dist/adapters/atlas/commands/_output_schema.js +0 -379
- package/dist/adapters/atlas/commands/actual/_logic.js +0 -116
- package/dist/adapters/atlas/commands/actual/index.js +0 -138
- package/dist/adapters/atlas/commands/auth.js +0 -1
- package/dist/adapters/atlas/commands/baseline/index.js +0 -137
- package/dist/adapters/atlas/commands/compare/_logic.js +0 -39
- package/dist/adapters/atlas/commands/compare/index.js +0 -89
- package/dist/adapters/atlas/commands/exec.js +0 -81
- package/dist/adapters/atlas/commands/project/index.js +0 -218
- package/dist/adapters/atlas/commands/schema.js +0 -25
- package/dist/adapters/atlas/commands/suggest.js +0 -83
- package/dist/adapters/atlas/commands/update.js +0 -104
- package/dist/adapters/atlas/daemon/index.js +0 -145
- package/dist/adapters/atlas/dict/index.js +0 -41
- package/dist/adapters/atlas/http/client.js +0 -200
- package/dist/adapters/atlas/http/index.js +0 -1
- package/dist/adapters/atlas/schema/actual.js +0 -16
- package/dist/adapters/atlas/schema/baseline.js +0 -34
- package/dist/adapters/atlas/schema/department.js +0 -11
- package/dist/adapters/atlas/schema/index.js +0 -4
- package/dist/adapters/atlas/schema/project.js +0 -13
- package/dist/adapters/atlas/util/cidr.js +0 -114
- package/dist/adapters/atlas/util/constants.js +0 -4
- package/dist/adapters/atlas/util/env.js +0 -56
- package/dist/adapters/atlas/util/environment.js +0 -152
- package/dist/adapters/atlas/util/errors.js +0 -49
- package/dist/adapters/atlas/util/helpers.js +0 -17
- package/dist/adapters/atlas/util/months.js +0 -65
- package/dist/adapters/atlas/util/output-limit.js +0 -21
- package/dist/adapters/atlas/util/output.js +0 -70
- package/dist/adapters/atlas/util/paths.js +0 -40
- package/dist/adapters/atlas/util/portable-store.js +0 -153
- package/dist/adapters/atlas/util/secure-fs.js +0 -41
- package/dist/adapters/atlas/util/time.js +0 -17
- package/dist/adapters/atlas/util/version.js +0 -1
|
@@ -1,561 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
// Auth
|
|
4
|
-
import { authLoginCmd, authStatusCmd, authRefreshCmd, authExportCmd, authImportCmd, authDoctorCmd } from './commands/auth.js';
|
|
5
|
-
// Project commands (find, projects, link, unlink)
|
|
6
|
-
import { findCmd, projectsCmd, linkCmd, linkStatusCmd, unlinkCmd, } from './commands/project/index.js';
|
|
7
|
-
// Baseline commands (month, summary, export)
|
|
8
|
-
import { monthCmd as baselineMonthCmd, summaryCmd as baselineSummaryCmd, exportCmd as baselineExportCmd, } from './commands/baseline/index.js';
|
|
9
|
-
// Actual commands (list, show, month, summary, export)
|
|
10
|
-
import { showCmd as actualShowCmd, monthCmd as actualMonthCmd, summaryCmd as actualSummaryCmd, exportCmd as actualExportCmd, } from './commands/actual/index.js';
|
|
11
|
-
// Compare
|
|
12
|
-
import { compareCmd } from './commands/compare/index.js';
|
|
13
|
-
// Utility commands
|
|
14
|
-
import { daemonCmd } from './daemon/index.js';
|
|
15
|
-
import { schemaCommandsCmd } from './commands/schema.js';
|
|
16
|
-
import { execCmd } from './commands/exec.js';
|
|
17
|
-
import { suggestCmd } from './commands/suggest.js';
|
|
18
|
-
import { updateCmd } from './commands/update.js';
|
|
19
|
-
import { BanmaApiError, ConfigError, OutputTooLargeError, SessionExpiredError, isAtlasError, } from './util/errors.js';
|
|
20
|
-
import { isJsonMode, printError } from './util/output.js';
|
|
21
|
-
import { getOutputSchema } from './commands/_output_schema.js';
|
|
22
|
-
export function handleError(err) {
|
|
23
|
-
if (isJsonMode()) {
|
|
24
|
-
printError(err, { json: true });
|
|
25
|
-
process.exit(exitCodeFor(err));
|
|
26
|
-
}
|
|
27
|
-
if (err instanceof SessionExpiredError) {
|
|
28
|
-
console.error(err.message);
|
|
29
|
-
process.exit(2);
|
|
30
|
-
}
|
|
31
|
-
if (err instanceof ConfigError) {
|
|
32
|
-
console.error(`Config error: ${err.message}`);
|
|
33
|
-
process.exit(64);
|
|
34
|
-
}
|
|
35
|
-
if (err instanceof BanmaApiError) {
|
|
36
|
-
console.error(`Banma API error [${err.errCode}] ${err.errorMsg}`);
|
|
37
|
-
process.exit(3);
|
|
38
|
-
}
|
|
39
|
-
if (err instanceof OutputTooLargeError) {
|
|
40
|
-
const debug = process.env.DEBUG === '1';
|
|
41
|
-
console.error(debug ? err.stack ?? err.message : err.message);
|
|
42
|
-
process.exit(65);
|
|
43
|
-
}
|
|
44
|
-
const debug = process.env.DEBUG === '1';
|
|
45
|
-
console.error(err instanceof Error ? (debug ? err.stack ?? err.message : err.message) : String(err));
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
export function exitCodeFor(err) {
|
|
49
|
-
if (err instanceof SessionExpiredError)
|
|
50
|
-
return 2;
|
|
51
|
-
if (err instanceof BanmaApiError)
|
|
52
|
-
return 3;
|
|
53
|
-
if (err instanceof OutputTooLargeError)
|
|
54
|
-
return 65;
|
|
55
|
-
if (isAtlasError(err)) {
|
|
56
|
-
switch (err.code) {
|
|
57
|
-
case 'AMBIGUOUS_PROJECT': return 4;
|
|
58
|
-
case 'PROJECT_NOT_FOUND': return 5;
|
|
59
|
-
case 'RATE_LIMITED': return 6;
|
|
60
|
-
case 'NETWORK_ERROR': return 7;
|
|
61
|
-
case 'UPDATE_ERROR': return 8;
|
|
62
|
-
case 'CONFIG_ERROR': return 64;
|
|
63
|
-
case 'INTERACTIVE_REQUIRED': return 64;
|
|
64
|
-
default: return 1;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return 1;
|
|
68
|
-
}
|
|
69
|
-
export function emitDescribe(cmd) {
|
|
70
|
-
const path = [];
|
|
71
|
-
let cursor = cmd;
|
|
72
|
-
while (cursor && cursor.name() !== 'atlas') {
|
|
73
|
-
path.unshift(cursor.name());
|
|
74
|
-
cursor = cursor.parent;
|
|
75
|
-
}
|
|
76
|
-
const commandPath = ['atlas', ...path].join(' ');
|
|
77
|
-
const outSchema = getOutputSchema(commandPath);
|
|
78
|
-
const payload = {
|
|
79
|
-
command: commandPath,
|
|
80
|
-
description: cmd.description() ?? '',
|
|
81
|
-
options: cmd.options.map((o) => {
|
|
82
|
-
const opt = o;
|
|
83
|
-
return {
|
|
84
|
-
flags: opt.flags,
|
|
85
|
-
description: opt.description ?? '',
|
|
86
|
-
required: opt.mandatory ?? false,
|
|
87
|
-
...(opt.defaultValue !== undefined ? { default: opt.defaultValue } : {}),
|
|
88
|
-
};
|
|
89
|
-
}),
|
|
90
|
-
args: cmd.registeredArguments?.map((a) => ({
|
|
91
|
-
name: a.name(),
|
|
92
|
-
required: a.required,
|
|
93
|
-
})) ?? [],
|
|
94
|
-
subcommands: cmd.commands.map((c) => c.name()),
|
|
95
|
-
outputSchema: outSchema.jsonSchema,
|
|
96
|
-
};
|
|
97
|
-
const envelope = { ok: true, data: payload };
|
|
98
|
-
process.stdout.write(JSON.stringify(envelope) + '\n');
|
|
99
|
-
}
|
|
100
|
-
function addProjectOptions(cmd) {
|
|
101
|
-
return cmd
|
|
102
|
-
.option('--project-id <id>', '项目ID,精确名称或唯一子串(或使用 BANMA_PROJECT_ID 环境变量)')
|
|
103
|
-
.option('--refresh-projects', '解析 --project-id 前重新获取项目目录缓存');
|
|
104
|
-
}
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
// Registration functions
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
function registerAuthCommands(program) {
|
|
109
|
-
const auth = program.command('auth').description('SSO 会话管理');
|
|
110
|
-
auth
|
|
111
|
-
.command('login')
|
|
112
|
-
.description('打开浏览器完成 SSO 登录并持久化会话')
|
|
113
|
-
.option('--json', '输出 JSON 信封')
|
|
114
|
-
.action(async (opts) => {
|
|
115
|
-
try {
|
|
116
|
-
await authLoginCmd(opts);
|
|
117
|
-
}
|
|
118
|
-
catch (e) {
|
|
119
|
-
handleError(e);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
auth
|
|
123
|
-
.command('status')
|
|
124
|
-
.description('显示当前会话信息')
|
|
125
|
-
.option('--json', '输出 JSON')
|
|
126
|
-
.action(async (opts) => {
|
|
127
|
-
try {
|
|
128
|
-
await authStatusCmd(opts);
|
|
129
|
-
}
|
|
130
|
-
catch (e) {
|
|
131
|
-
handleError(e);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
auth
|
|
135
|
-
.command('refresh')
|
|
136
|
-
.description('无头模式静默刷新 access_token(依赖 SSO_REFRESH_TOKEN,失败降级到 atlas auth login)')
|
|
137
|
-
.option('--json', '输出 JSON 信封')
|
|
138
|
-
.action(async (opts) => {
|
|
139
|
-
try {
|
|
140
|
-
await authRefreshCmd(opts);
|
|
141
|
-
}
|
|
142
|
-
catch (e) {
|
|
143
|
-
handleError(e);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
auth
|
|
147
|
-
.command('export')
|
|
148
|
-
.description('导出登录态为便携 bundle(默认 --refresh-only 剥离短命 token)')
|
|
149
|
-
.option('-o, --out <path>', '输出文件路径(省略时 --base64 写 stdout)')
|
|
150
|
-
.option('--base64', 'base64 文本格式(gzip+JSON),方便管道/env 传输')
|
|
151
|
-
.option('--refresh-only', '仅长期凭证(默认开启)', true)
|
|
152
|
-
.option('--include-access-token', '包含短命 access_token(默认剥离)')
|
|
153
|
-
.option('--json', '输出 JSON 信封')
|
|
154
|
-
.action(async (opts) => {
|
|
155
|
-
try {
|
|
156
|
-
await authExportCmd(opts);
|
|
157
|
-
}
|
|
158
|
-
catch (e) {
|
|
159
|
-
handleError(e);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
auth
|
|
163
|
-
.command('import')
|
|
164
|
-
.description('从便携 bundle 导入登录态(覆盖本地 cookies)')
|
|
165
|
-
.option('-i, --input <path>', '导入文件路径(省略 --base64 时从 stdin 读)')
|
|
166
|
-
.option('--base64', '输入为 base64 文本格式')
|
|
167
|
-
.option('--force', '本地已有登录态时强制覆盖')
|
|
168
|
-
.option('--persist', '落盘到 ~/.atlas/cookies.json(默认开启)', true)
|
|
169
|
-
.option('--json', '输出 JSON 信封')
|
|
170
|
-
.action(async (opts) => {
|
|
171
|
-
try {
|
|
172
|
-
await authImportCmd(opts);
|
|
173
|
-
}
|
|
174
|
-
catch (e) {
|
|
175
|
-
handleError(e);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
auth
|
|
179
|
-
.command('doctor')
|
|
180
|
-
.description('诊断当前环境的登录路径(沙盒 agent 自省用)')
|
|
181
|
-
.option('--json', '输出 JSON 信封')
|
|
182
|
-
.action(async (opts) => {
|
|
183
|
-
try {
|
|
184
|
-
await authDoctorCmd(opts);
|
|
185
|
-
}
|
|
186
|
-
catch (e) {
|
|
187
|
-
handleError(e);
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
function registerProjectCommands(program) {
|
|
192
|
-
// atlas find
|
|
193
|
-
program
|
|
194
|
-
.command('find <kind> <query>')
|
|
195
|
-
.description('搜索项目/部门/字典值(kind: project|department|manpower-type|role|area)')
|
|
196
|
-
.option('--json', '输出 JSON 信封')
|
|
197
|
-
.option('--refresh', '刷新字典/部门/项目缓存')
|
|
198
|
-
.option('--limit <n>', '最多返回 N 个候选(默认 20)')
|
|
199
|
-
.addHelpText('after', `
|
|
200
|
-
kind 说明:
|
|
201
|
-
|
|
202
|
-
project 搜索有权限的项目列表(从 API 实时查询)
|
|
203
|
-
department 搜索部门树中的部门名称(1500+ 部门节点)
|
|
204
|
-
manpower-type 搜索人力类型(例: "斑马"、"智软",baseline --manpower-type 用)
|
|
205
|
-
role 搜索人力基线角色(例: "开发"、"产品"、"测试"、"PM"、"设计" 等)
|
|
206
|
-
area 搜索地域(例: "北上杭"、"合肥"、"武汉"、"西南")`)
|
|
207
|
-
.action(async (kind, query, opts) => {
|
|
208
|
-
try {
|
|
209
|
-
await findCmd(kind, query, opts);
|
|
210
|
-
}
|
|
211
|
-
catch (e) {
|
|
212
|
-
handleError(e);
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
// atlas projects
|
|
216
|
-
program
|
|
217
|
-
.command('projects')
|
|
218
|
-
.description('列出我有权限的所有项目')
|
|
219
|
-
.option('--json', '输出 JSON 信封')
|
|
220
|
-
.option('--refresh', '刷新项目缓存')
|
|
221
|
-
.action(async (opts) => {
|
|
222
|
-
try {
|
|
223
|
-
await projectsCmd(opts);
|
|
224
|
-
}
|
|
225
|
-
catch (e) {
|
|
226
|
-
handleError(e);
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
// atlas link
|
|
230
|
-
program
|
|
231
|
-
.command('link [project]')
|
|
232
|
-
.description('绑定当前项目(精确名称/子串/数字ID)。不带参数时显示当前绑定状态')
|
|
233
|
-
.option('--json', '输出 JSON 信封')
|
|
234
|
-
.option('--dry-run', '仅预览,不实际写入绑定')
|
|
235
|
-
.option('--refresh-projects', '解析 project 前重新获取项目目录缓存')
|
|
236
|
-
.action(async (project, opts) => {
|
|
237
|
-
try {
|
|
238
|
-
if (project === undefined) {
|
|
239
|
-
await linkStatusCmd(opts);
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
await linkCmd(project, opts);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
catch (e) {
|
|
246
|
-
handleError(e);
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
// atlas unlink
|
|
250
|
-
program
|
|
251
|
-
.command('unlink')
|
|
252
|
-
.description('清除当前项目绑定')
|
|
253
|
-
.option('--json', '输出 JSON 信封')
|
|
254
|
-
.option('--dry-run', '仅预览,不实际清除绑定')
|
|
255
|
-
.action(async (opts) => {
|
|
256
|
-
try {
|
|
257
|
-
await unlinkCmd(opts);
|
|
258
|
-
}
|
|
259
|
-
catch (e) {
|
|
260
|
-
handleError(e);
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
function registerBaselineCommands(program) {
|
|
265
|
-
const base = program.command('baseline').description('基线(计划)人力数据');
|
|
266
|
-
// atlas baseline month
|
|
267
|
-
addProjectOptions(base
|
|
268
|
-
.command('month')
|
|
269
|
-
.description('人力基线汇总(按月显示人力投入)'))
|
|
270
|
-
.option('--json', '输出 JSON')
|
|
271
|
-
.option('--department <name>', '按部门名称/ID 筛选(子串,不区分大小写)')
|
|
272
|
-
.option('--role <name>', '按角色/备注筛选(子串,不区分大小写)')
|
|
273
|
-
.option('--area <code>', '按地域筛选(子串,不区分大小写)')
|
|
274
|
-
.option('--manpower-type <type>', '按人力类型筛选(子串,不区分大小写)')
|
|
275
|
-
.option('--month <yyyymm>', '查询月份(YYYY-MM,与 --from/--to 互斥)')
|
|
276
|
-
.option('--from <yyyymm>', '起始月份(YYYY-MM,包含)')
|
|
277
|
-
.option('--to <yyyymm>', '结束月份(YYYY-MM,包含)')
|
|
278
|
-
.option('--all-months', '显示所有月份(默认:只显示有人力的月份)')
|
|
279
|
-
.action(async (opts) => {
|
|
280
|
-
try {
|
|
281
|
-
await baselineMonthCmd(opts);
|
|
282
|
-
}
|
|
283
|
-
catch (e) {
|
|
284
|
-
handleError(e);
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
// atlas baseline summary
|
|
288
|
-
addProjectOptions(base
|
|
289
|
-
.command('summary')
|
|
290
|
-
.description('按月/部门/角色汇总基线人力投入'))
|
|
291
|
-
.option('--by <axis>', 'month | department | role', 'month')
|
|
292
|
-
.option('--department <name>', '按部门名称/ID 筛选(子串,不区分大小写)')
|
|
293
|
-
.option('--role <name>', '按角色/备注筛选(子串,不区分大小写)')
|
|
294
|
-
.option('--area <code>', '按地域筛选(子串,不区分大小写)')
|
|
295
|
-
.option('--manpower-type <type>', '按人力类型筛选(子串,不区分大小写)')
|
|
296
|
-
.option('--from <yyyymm>', '起始月份(YYYY-MM,包含)')
|
|
297
|
-
.option('--to <yyyymm>', '结束月份(YYYY-MM,包含)')
|
|
298
|
-
.option('--json', '输出 JSON')
|
|
299
|
-
.action(async (opts) => {
|
|
300
|
-
try {
|
|
301
|
-
await baselineSummaryCmd(opts);
|
|
302
|
-
}
|
|
303
|
-
catch (e) {
|
|
304
|
-
handleError(e);
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
// atlas baseline export
|
|
308
|
-
addProjectOptions(base
|
|
309
|
-
.command('export')
|
|
310
|
-
.description('导出基线条目到 CSV/JSON'))
|
|
311
|
-
.requiredOption('--format <fmt>', 'csv | json')
|
|
312
|
-
.requiredOption('--out <path>', '输出文件路径')
|
|
313
|
-
.option('--from <yyyymm>', '起始月份(YYYY-MM,包含)')
|
|
314
|
-
.option('--to <yyyymm>', '结束月份(YYYY-MM,包含)')
|
|
315
|
-
.option('--department <name>', '按部门名称/ID 筛选(子串,不区分大小写)')
|
|
316
|
-
.option('--role <name>', '按角色/备注筛选(子串,不区分大小写)')
|
|
317
|
-
.option('--since <iso>', '仅导出指定时间后修改的条目(ISO 时间戳)')
|
|
318
|
-
.option('--json', '输出 JSON 信封(结果摘要)')
|
|
319
|
-
.action(async (opts) => {
|
|
320
|
-
try {
|
|
321
|
-
if (!['csv', 'json'].includes(opts.format)) {
|
|
322
|
-
throw new ConfigError(`--format must be csv|json, got "${opts.format}"`);
|
|
323
|
-
}
|
|
324
|
-
await baselineExportCmd(opts);
|
|
325
|
-
}
|
|
326
|
-
catch (e) {
|
|
327
|
-
handleError(e);
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
function registerActualCommands(program) {
|
|
332
|
-
const base = program.command('actual').description('实际人力数据');
|
|
333
|
-
// atlas actual show <staffId>
|
|
334
|
-
addProjectOptions(base
|
|
335
|
-
.command('show <staffId>')
|
|
336
|
-
.description('查看单个人员的实际工时明细'))
|
|
337
|
-
.option('--month <yyyymm>', '查询月份(YYYY-MM,默认当前月)')
|
|
338
|
-
.option('--status <status>', '筛选审批状态: approved | pending | all', 'approved')
|
|
339
|
-
.option('--json', '输出 JSON 信封')
|
|
340
|
-
.action(async (staffId, opts) => {
|
|
341
|
-
try {
|
|
342
|
-
await actualShowCmd(staffId, opts);
|
|
343
|
-
}
|
|
344
|
-
catch (e) {
|
|
345
|
-
handleError(e);
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
// atlas actual month
|
|
349
|
-
addProjectOptions(base
|
|
350
|
-
.command('month')
|
|
351
|
-
.description('实际工时明细(人员×周透视表)。无参数时默认查当前自然年'))
|
|
352
|
-
.option('--month <yyyymm>', '查询月份(YYYY-MM,与 --from/--to 互斥)')
|
|
353
|
-
.option('--from <yyyymm>', '起始月份(YYYY-MM,包含,与 --month 互斥)')
|
|
354
|
-
.option('--to <yyyymm>', '结束月份(YYYY-MM,包含,与 --month 互斥)')
|
|
355
|
-
.option('--status <status>', '筛选审批状态: approved | pending | all', 'approved')
|
|
356
|
-
.option('--department <name>', '按团队负责人/部门筛选(子串,不区分大小写)')
|
|
357
|
-
.option('--role <name>', '按角色/备注筛选(子串,不区分大小写)')
|
|
358
|
-
.option('--staff-name <name>', '按姓名/工号筛选(子串,不区分大小写)')
|
|
359
|
-
.option('--json', '输出 JSON 信封')
|
|
360
|
-
.action(async (opts) => {
|
|
361
|
-
try {
|
|
362
|
-
await actualMonthCmd(opts);
|
|
363
|
-
}
|
|
364
|
-
catch (e) {
|
|
365
|
-
handleError(e);
|
|
366
|
-
}
|
|
367
|
-
});
|
|
368
|
-
// atlas actual summary
|
|
369
|
-
addProjectOptions(base
|
|
370
|
-
.command('summary')
|
|
371
|
-
.description('按月/部门/角色汇总实际工时'))
|
|
372
|
-
.option('--by <axis>', 'month | department | role', 'month')
|
|
373
|
-
.option('--month <yyyymm>', '查询月份')
|
|
374
|
-
.option('--status <status>', 'approved | pending | all', 'approved')
|
|
375
|
-
.option('--department <name>', '按部门筛选')
|
|
376
|
-
.option('--role <name>', '按角色筛选')
|
|
377
|
-
.option('--from <yyyymm>', '起始月份')
|
|
378
|
-
.option('--to <yyyymm>', '结束月份')
|
|
379
|
-
.option('--json', '输出 JSON 信封')
|
|
380
|
-
.action(async (opts) => {
|
|
381
|
-
try {
|
|
382
|
-
await actualSummaryCmd(opts);
|
|
383
|
-
}
|
|
384
|
-
catch (e) {
|
|
385
|
-
handleError(e);
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
// atlas actual export
|
|
389
|
-
addProjectOptions(base
|
|
390
|
-
.command('export')
|
|
391
|
-
.description('导出实际工时数据(CSV/JSON)'))
|
|
392
|
-
.requiredOption('--format <fmt>', 'csv | json')
|
|
393
|
-
.requiredOption('--out <path>', '输出文件路径')
|
|
394
|
-
.option('--by <axis>', 'month | department | role', 'month')
|
|
395
|
-
.option('--status <status>', 'approved | pending | all', 'approved')
|
|
396
|
-
.option('--department <name>', '按部门筛选')
|
|
397
|
-
.option('--role <name>', '按角色筛选')
|
|
398
|
-
.option('--from <yyyymm>', '起始月份')
|
|
399
|
-
.option('--to <yyyymm>', '结束月份')
|
|
400
|
-
.option('--json', '输出 JSON 信封')
|
|
401
|
-
.action(async (opts) => {
|
|
402
|
-
try {
|
|
403
|
-
await actualExportCmd(opts);
|
|
404
|
-
}
|
|
405
|
-
catch (e) {
|
|
406
|
-
handleError(e);
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
function registerCompareCommands(program) {
|
|
411
|
-
addProjectOptions(program
|
|
412
|
-
.command('compare')
|
|
413
|
-
.description('Compare baseline (计划) vs actual (实际) manpower'))
|
|
414
|
-
.option('--by <axis>', 'month | department | role', 'month')
|
|
415
|
-
.option('--from <yyyymm>', '起始月份(YYYY-MM,包含)')
|
|
416
|
-
.option('--to <yyyymm>', '结束月份(YYYY-MM,包含)')
|
|
417
|
-
.option('--month <yyyymm>', '查询月份(YYYY-MM,优先级高于 from/to 用于实际数据 API)')
|
|
418
|
-
.option('--department <name>', '按部门名称/ID 筛选(子串,不区分大小写)')
|
|
419
|
-
.option('--role <name>', '按角色/备注筛选(子串,不区分大小写)')
|
|
420
|
-
.option('--status <status>', '筛选审批状态: approved | pending | all', 'approved')
|
|
421
|
-
.option('--threshold <n>', '差异绝对值阈值(人月),低于此值不标记', '0')
|
|
422
|
-
.option('--flag-overrun', '用 ⚠️ 标记实际 > 基线的情况')
|
|
423
|
-
.option('--page <n>', '页码(从 1 开始)')
|
|
424
|
-
.option('--page-size <n>', '每页条目数(大于 0 时启用分页)')
|
|
425
|
-
.option('--json', '输出 JSON 信封')
|
|
426
|
-
.action(async (opts) => {
|
|
427
|
-
try {
|
|
428
|
-
await compareCmd(opts);
|
|
429
|
-
}
|
|
430
|
-
catch (e) {
|
|
431
|
-
handleError(e);
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
function registerUtilityCommands(program) {
|
|
436
|
-
// daemon
|
|
437
|
-
program
|
|
438
|
-
.command('daemon')
|
|
439
|
-
.description('启动本地守护进程(沙盒环境使用,保持浏览器会话)')
|
|
440
|
-
.option('--port <n>', '监听端口(默认 8765,也可用 ATLAS_DAEMON_PORT 环境变量)')
|
|
441
|
-
.option('--host <host>', '监听地址(默认 127.0.0.1;非 loopback 需配 --allow-ip 或 --insecure)')
|
|
442
|
-
.option('--allow-ip <cidr>', 'IP 白名单(CIDR 或单 IP,可多次传)', (v, p) => [...p, v], [])
|
|
443
|
-
.option('--tls-cert <path>', 'TLS 证书文件路径(需与 --tls-key 同时指定)')
|
|
444
|
-
.option('--tls-key <path>', 'TLS 私钥文件路径')
|
|
445
|
-
.option('--insecure', '显式允许明文监听非 loopback 接口(仅本机开发)')
|
|
446
|
-
.option('--json', '输出 JSON 信封')
|
|
447
|
-
.action(async (opts) => {
|
|
448
|
-
try {
|
|
449
|
-
await daemonCmd(opts);
|
|
450
|
-
}
|
|
451
|
-
catch (e) {
|
|
452
|
-
handleError(e);
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
// schema
|
|
456
|
-
const schema = program.command('schema').description('CLI 自省 / 字段字典导出');
|
|
457
|
-
schema
|
|
458
|
-
.command('commands')
|
|
459
|
-
.description('列出所有命令的参数 schema 及输出 schema 标注')
|
|
460
|
-
.option('--json', '输出 JSON 信封')
|
|
461
|
-
.option('--describe', '输出完整命令 schema 含 outputSchema(agent 自省用)')
|
|
462
|
-
.action((opts) => {
|
|
463
|
-
try {
|
|
464
|
-
schemaCommandsCmd(program, opts);
|
|
465
|
-
}
|
|
466
|
-
catch (e) {
|
|
467
|
-
handleError(e);
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
// exec
|
|
471
|
-
program
|
|
472
|
-
.command('exec')
|
|
473
|
-
.description('按 plan-file 顺序执行多条命令(agent 批处理用)')
|
|
474
|
-
.requiredOption('--plan-file <path>', 'JSON 计划文件路径')
|
|
475
|
-
.option('--json', '输出 JSON 信封')
|
|
476
|
-
.action(async (opts) => {
|
|
477
|
-
try {
|
|
478
|
-
await execCmd(opts);
|
|
479
|
-
}
|
|
480
|
-
catch (e) {
|
|
481
|
-
handleError(e);
|
|
482
|
-
}
|
|
483
|
-
});
|
|
484
|
-
// update
|
|
485
|
-
program
|
|
486
|
-
.command('update')
|
|
487
|
-
.description('检查并自动升级到最新版本(设置 ATLAS_DISABLE_UPDATE=1 禁用)')
|
|
488
|
-
.option('--json', '输出 JSON 信封')
|
|
489
|
-
.action(async (opts) => {
|
|
490
|
-
try {
|
|
491
|
-
await updateCmd(opts);
|
|
492
|
-
}
|
|
493
|
-
catch (e) {
|
|
494
|
-
handleError(e);
|
|
495
|
-
}
|
|
496
|
-
});
|
|
497
|
-
// suggest
|
|
498
|
-
program
|
|
499
|
-
.command('suggest <query...>')
|
|
500
|
-
.description('将自然语言查询翻译为候选 atlas 命令(纯规则,不调 LLM)')
|
|
501
|
-
.option('--json', '输出 JSON 信封')
|
|
502
|
-
.action((tokens, opts) => {
|
|
503
|
-
try {
|
|
504
|
-
suggestCmd(tokens.join(' '), opts);
|
|
505
|
-
}
|
|
506
|
-
catch (e) {
|
|
507
|
-
handleError(e);
|
|
508
|
-
}
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
// Version is generated from package.json by scripts/sync-version.mjs (run as
|
|
512
|
-
// prebuild). Inlined as a string constant so both `tsc` and bun --compile
|
|
513
|
-
// single-file binaries can read it without runtime fs access.
|
|
514
|
-
import { ATLAS_VERSION } from './util/version.js';
|
|
515
|
-
export function buildProgram() {
|
|
516
|
-
const program = new Command();
|
|
517
|
-
program
|
|
518
|
-
.name('atlas')
|
|
519
|
-
.description('Atlas CLI - 斑马云图人力基线管理工具')
|
|
520
|
-
.version(ATLAS_VERSION, '-V, --version', '显示版本号')
|
|
521
|
-
.option('--json', '以 JSON 信封输出(也可用环境变量 ATLAS_OUTPUT=json)')
|
|
522
|
-
.option('--quiet', '静默模式:抑制非信封输出(设置 ATLAS_QUIET=1 也可)')
|
|
523
|
-
.option('--describe', '不执行命令,仅输出该命令的参数 schema(agent 自省用)')
|
|
524
|
-
.addHelpText('after', `
|
|
525
|
-
退出码:
|
|
526
|
-
0 成功
|
|
527
|
-
1 通用错误 / exec 中某 step 失败
|
|
528
|
-
2 会话过期(需重新登录)
|
|
529
|
-
3 API 返回错误(BanmaApiError)
|
|
530
|
-
4 项目匹配歧义(AmbiguousProject)
|
|
531
|
-
5 项目未找到(ProjectNotFound)
|
|
532
|
-
6 API 限流(RateLimited)
|
|
533
|
-
7 网络错误(NetworkError)
|
|
534
|
-
8 版本更新异常(UpdateError)
|
|
535
|
-
64 配置错误(ConfigError)
|
|
536
|
-
65 输出超限(OutputTooLargeError)
|
|
537
|
-
`)
|
|
538
|
-
.showHelpAfterError()
|
|
539
|
-
.hook('preAction', (thisCommand, actionCommand) => {
|
|
540
|
-
const opts = thisCommand.opts();
|
|
541
|
-
if (opts.json === true && process.env.ATLAS_OUTPUT === undefined) {
|
|
542
|
-
process.env.ATLAS_OUTPUT = 'json';
|
|
543
|
-
}
|
|
544
|
-
if (opts.quiet === true) {
|
|
545
|
-
process.env.ATLAS_QUIET = '1';
|
|
546
|
-
}
|
|
547
|
-
if (opts.describe === true) {
|
|
548
|
-
emitDescribe(actionCommand);
|
|
549
|
-
process.exit(0);
|
|
550
|
-
}
|
|
551
|
-
});
|
|
552
|
-
registerAuthCommands(program);
|
|
553
|
-
registerProjectCommands(program);
|
|
554
|
-
registerBaselineCommands(program);
|
|
555
|
-
registerActualCommands(program);
|
|
556
|
-
registerCompareCommands(program);
|
|
557
|
-
registerUtilityCommands(program);
|
|
558
|
-
return program;
|
|
559
|
-
}
|
|
560
|
-
// 注意:auto-run 入口已移至 bin/atlas.ts
|
|
561
|
-
// 此模块仅 export buildProgram / handleError / emitDescribe
|