@dreamor/atlas-cli 0.7.22 → 0.7.24

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.
Files changed (43) hide show
  1. package/README.github.md +230 -0
  2. package/README.md +1 -55
  3. package/atlas.cjs +226 -0
  4. package/package.json +7 -9
  5. package/atlas.js +0 -5
  6. package/dist/adapters/atlas/auth/browser.js +0 -37
  7. package/dist/adapters/atlas/auth/index.js +0 -3
  8. package/dist/adapters/atlas/auth/login.js +0 -147
  9. package/dist/adapters/atlas/auth/refresh.js +0 -138
  10. package/dist/adapters/atlas/auth/session.js +0 -115
  11. package/dist/adapters/atlas/cli.js +0 -512
  12. package/dist/adapters/atlas/commands/_output_schema.js +0 -379
  13. package/dist/adapters/atlas/commands/actual/_logic.js +0 -116
  14. package/dist/adapters/atlas/commands/actual/index.js +0 -138
  15. package/dist/adapters/atlas/commands/auth.js +0 -1
  16. package/dist/adapters/atlas/commands/baseline/index.js +0 -137
  17. package/dist/adapters/atlas/commands/compare/_logic.js +0 -39
  18. package/dist/adapters/atlas/commands/compare/index.js +0 -89
  19. package/dist/adapters/atlas/commands/exec.js +0 -81
  20. package/dist/adapters/atlas/commands/project/index.js +0 -218
  21. package/dist/adapters/atlas/commands/schema.js +0 -25
  22. package/dist/adapters/atlas/commands/suggest.js +0 -83
  23. package/dist/adapters/atlas/commands/update.js +0 -104
  24. package/dist/adapters/atlas/daemon/index.js +0 -64
  25. package/dist/adapters/atlas/dict/index.js +0 -41
  26. package/dist/adapters/atlas/http/client.js +0 -200
  27. package/dist/adapters/atlas/http/index.js +0 -1
  28. package/dist/adapters/atlas/schema/actual.js +0 -16
  29. package/dist/adapters/atlas/schema/baseline.js +0 -34
  30. package/dist/adapters/atlas/schema/department.js +0 -11
  31. package/dist/adapters/atlas/schema/index.js +0 -4
  32. package/dist/adapters/atlas/schema/project.js +0 -13
  33. package/dist/adapters/atlas/util/constants.js +0 -4
  34. package/dist/adapters/atlas/util/env.js +0 -56
  35. package/dist/adapters/atlas/util/errors.js +0 -49
  36. package/dist/adapters/atlas/util/helpers.js +0 -17
  37. package/dist/adapters/atlas/util/months.js +0 -65
  38. package/dist/adapters/atlas/util/output-limit.js +0 -21
  39. package/dist/adapters/atlas/util/output.js +0 -70
  40. package/dist/adapters/atlas/util/paths.js +0 -40
  41. package/dist/adapters/atlas/util/secure-fs.js +0 -41
  42. package/dist/adapters/atlas/util/time.js +0 -17
  43. package/dist/adapters/atlas/util/version.js +0 -1
@@ -1,512 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- // Auth
4
- import { authLoginCmd, authStatusCmd, authRefreshCmd } 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
- }
147
- function registerProjectCommands(program) {
148
- // atlas find
149
- program
150
- .command('find <kind> <query>')
151
- .description('搜索项目/部门/字典值(kind: project|department|manpower-type|role|area)')
152
- .option('--json', '输出 JSON 信封')
153
- .option('--refresh', '刷新字典/部门/项目缓存')
154
- .option('--limit <n>', '最多返回 N 个候选(默认 20)')
155
- .addHelpText('after', `
156
- kind 说明:
157
-
158
- project 搜索有权限的项目列表(从 API 实时查询)
159
- department 搜索部门树中的部门名称(1500+ 部门节点)
160
- manpower-type 搜索人力类型(例: "斑马"、"智软",baseline --manpower-type 用)
161
- role 搜索人力基线角色(例: "开发"、"产品"、"测试"、"PM"、"设计" 等)
162
- area 搜索地域(例: "北上杭"、"合肥"、"武汉"、"西南")`)
163
- .action(async (kind, query, opts) => {
164
- try {
165
- await findCmd(kind, query, opts);
166
- }
167
- catch (e) {
168
- handleError(e);
169
- }
170
- });
171
- // atlas projects
172
- program
173
- .command('projects')
174
- .description('列出我有权限的所有项目')
175
- .option('--json', '输出 JSON 信封')
176
- .option('--refresh', '刷新项目缓存')
177
- .action(async (opts) => {
178
- try {
179
- await projectsCmd(opts);
180
- }
181
- catch (e) {
182
- handleError(e);
183
- }
184
- });
185
- // atlas link
186
- program
187
- .command('link [project]')
188
- .description('绑定当前项目(精确名称/子串/数字ID)。不带参数时显示当前绑定状态')
189
- .option('--json', '输出 JSON 信封')
190
- .option('--dry-run', '仅预览,不实际写入绑定')
191
- .option('--refresh-projects', '解析 project 前重新获取项目目录缓存')
192
- .action(async (project, opts) => {
193
- try {
194
- if (project === undefined) {
195
- await linkStatusCmd(opts);
196
- }
197
- else {
198
- await linkCmd(project, opts);
199
- }
200
- }
201
- catch (e) {
202
- handleError(e);
203
- }
204
- });
205
- // atlas unlink
206
- program
207
- .command('unlink')
208
- .description('清除当前项目绑定')
209
- .option('--json', '输出 JSON 信封')
210
- .option('--dry-run', '仅预览,不实际清除绑定')
211
- .action(async (opts) => {
212
- try {
213
- await unlinkCmd(opts);
214
- }
215
- catch (e) {
216
- handleError(e);
217
- }
218
- });
219
- }
220
- function registerBaselineCommands(program) {
221
- const base = program.command('baseline').description('基线(计划)人力数据');
222
- // atlas baseline month
223
- addProjectOptions(base
224
- .command('month')
225
- .description('人力基线汇总(按月显示人力投入)'))
226
- .option('--json', '输出 JSON')
227
- .option('--department <name>', '按部门名称/ID 筛选(子串,不区分大小写)')
228
- .option('--role <name>', '按角色/备注筛选(子串,不区分大小写)')
229
- .option('--area <code>', '按地域筛选(子串,不区分大小写)')
230
- .option('--manpower-type <type>', '按人力类型筛选(子串,不区分大小写)')
231
- .option('--month <yyyymm>', '查询月份(YYYY-MM,与 --from/--to 互斥)')
232
- .option('--from <yyyymm>', '起始月份(YYYY-MM,包含)')
233
- .option('--to <yyyymm>', '结束月份(YYYY-MM,包含)')
234
- .option('--all-months', '显示所有月份(默认:只显示有人力的月份)')
235
- .action(async (opts) => {
236
- try {
237
- await baselineMonthCmd(opts);
238
- }
239
- catch (e) {
240
- handleError(e);
241
- }
242
- });
243
- // atlas baseline summary
244
- addProjectOptions(base
245
- .command('summary')
246
- .description('按月/部门/角色汇总基线人力投入'))
247
- .option('--by <axis>', 'month | department | role', 'month')
248
- .option('--department <name>', '按部门名称/ID 筛选(子串,不区分大小写)')
249
- .option('--role <name>', '按角色/备注筛选(子串,不区分大小写)')
250
- .option('--area <code>', '按地域筛选(子串,不区分大小写)')
251
- .option('--manpower-type <type>', '按人力类型筛选(子串,不区分大小写)')
252
- .option('--from <yyyymm>', '起始月份(YYYY-MM,包含)')
253
- .option('--to <yyyymm>', '结束月份(YYYY-MM,包含)')
254
- .option('--json', '输出 JSON')
255
- .action(async (opts) => {
256
- try {
257
- await baselineSummaryCmd(opts);
258
- }
259
- catch (e) {
260
- handleError(e);
261
- }
262
- });
263
- // atlas baseline export
264
- addProjectOptions(base
265
- .command('export')
266
- .description('导出基线条目到 CSV/JSON'))
267
- .requiredOption('--format <fmt>', 'csv | json')
268
- .requiredOption('--out <path>', '输出文件路径')
269
- .option('--from <yyyymm>', '起始月份(YYYY-MM,包含)')
270
- .option('--to <yyyymm>', '结束月份(YYYY-MM,包含)')
271
- .option('--department <name>', '按部门名称/ID 筛选(子串,不区分大小写)')
272
- .option('--role <name>', '按角色/备注筛选(子串,不区分大小写)')
273
- .option('--since <iso>', '仅导出指定时间后修改的条目(ISO 时间戳)')
274
- .option('--json', '输出 JSON 信封(结果摘要)')
275
- .action(async (opts) => {
276
- try {
277
- if (!['csv', 'json'].includes(opts.format)) {
278
- throw new ConfigError(`--format must be csv|json, got "${opts.format}"`);
279
- }
280
- await baselineExportCmd(opts);
281
- }
282
- catch (e) {
283
- handleError(e);
284
- }
285
- });
286
- }
287
- function registerActualCommands(program) {
288
- const base = program.command('actual').description('实际人力数据');
289
- // atlas actual show <staffId>
290
- addProjectOptions(base
291
- .command('show <staffId>')
292
- .description('查看单个人员的实际工时明细'))
293
- .option('--month <yyyymm>', '查询月份(YYYY-MM,默认当前月)')
294
- .option('--status <status>', '筛选审批状态: approved | pending | all', 'approved')
295
- .option('--json', '输出 JSON 信封')
296
- .action(async (staffId, opts) => {
297
- try {
298
- await actualShowCmd(staffId, opts);
299
- }
300
- catch (e) {
301
- handleError(e);
302
- }
303
- });
304
- // atlas actual month
305
- addProjectOptions(base
306
- .command('month')
307
- .description('实际工时明细(人员×周透视表)。无参数时默认查当前自然年'))
308
- .option('--month <yyyymm>', '查询月份(YYYY-MM,与 --from/--to 互斥)')
309
- .option('--from <yyyymm>', '起始月份(YYYY-MM,包含,与 --month 互斥)')
310
- .option('--to <yyyymm>', '结束月份(YYYY-MM,包含,与 --month 互斥)')
311
- .option('--status <status>', '筛选审批状态: approved | pending | all', 'approved')
312
- .option('--department <name>', '按团队负责人/部门筛选(子串,不区分大小写)')
313
- .option('--role <name>', '按角色/备注筛选(子串,不区分大小写)')
314
- .option('--staff-name <name>', '按姓名/工号筛选(子串,不区分大小写)')
315
- .option('--json', '输出 JSON 信封')
316
- .action(async (opts) => {
317
- try {
318
- await actualMonthCmd(opts);
319
- }
320
- catch (e) {
321
- handleError(e);
322
- }
323
- });
324
- // atlas actual summary
325
- addProjectOptions(base
326
- .command('summary')
327
- .description('按月/部门/角色汇总实际工时'))
328
- .option('--by <axis>', 'month | department | role', 'month')
329
- .option('--month <yyyymm>', '查询月份')
330
- .option('--status <status>', 'approved | pending | all', 'approved')
331
- .option('--department <name>', '按部门筛选')
332
- .option('--role <name>', '按角色筛选')
333
- .option('--from <yyyymm>', '起始月份')
334
- .option('--to <yyyymm>', '结束月份')
335
- .option('--json', '输出 JSON 信封')
336
- .action(async (opts) => {
337
- try {
338
- await actualSummaryCmd(opts);
339
- }
340
- catch (e) {
341
- handleError(e);
342
- }
343
- });
344
- // atlas actual export
345
- addProjectOptions(base
346
- .command('export')
347
- .description('导出实际工时数据(CSV/JSON)'))
348
- .requiredOption('--format <fmt>', 'csv | json')
349
- .requiredOption('--out <path>', '输出文件路径')
350
- .option('--by <axis>', 'month | department | role', 'month')
351
- .option('--status <status>', 'approved | pending | all', 'approved')
352
- .option('--department <name>', '按部门筛选')
353
- .option('--role <name>', '按角色筛选')
354
- .option('--from <yyyymm>', '起始月份')
355
- .option('--to <yyyymm>', '结束月份')
356
- .option('--json', '输出 JSON 信封')
357
- .action(async (opts) => {
358
- try {
359
- await actualExportCmd(opts);
360
- }
361
- catch (e) {
362
- handleError(e);
363
- }
364
- });
365
- }
366
- function registerCompareCommands(program) {
367
- addProjectOptions(program
368
- .command('compare')
369
- .description('Compare baseline (计划) vs actual (实际) manpower'))
370
- .option('--by <axis>', 'month | department | role', 'month')
371
- .option('--from <yyyymm>', '起始月份(YYYY-MM,包含)')
372
- .option('--to <yyyymm>', '结束月份(YYYY-MM,包含)')
373
- .option('--month <yyyymm>', '查询月份(YYYY-MM,优先级高于 from/to 用于实际数据 API)')
374
- .option('--department <name>', '按部门名称/ID 筛选(子串,不区分大小写)')
375
- .option('--role <name>', '按角色/备注筛选(子串,不区分大小写)')
376
- .option('--status <status>', '筛选审批状态: approved | pending | all', 'approved')
377
- .option('--threshold <n>', '差异绝对值阈值(人月),低于此值不标记', '0')
378
- .option('--flag-overrun', '用 ⚠️ 标记实际 > 基线的情况')
379
- .option('--page <n>', '页码(从 1 开始)')
380
- .option('--page-size <n>', '每页条目数(大于 0 时启用分页)')
381
- .option('--json', '输出 JSON 信封')
382
- .action(async (opts) => {
383
- try {
384
- await compareCmd(opts);
385
- }
386
- catch (e) {
387
- handleError(e);
388
- }
389
- });
390
- }
391
- function registerUtilityCommands(program) {
392
- // daemon
393
- program
394
- .command('daemon')
395
- .description('启动本地守护进程(沙盒环境使用,保持浏览器会话)')
396
- .option('--port <n>', '监听端口(默认 8765,也可用 ATLAS_DAEMON_PORT 环境变量)')
397
- .option('--json', '输出 JSON 信封')
398
- .action(async (opts) => {
399
- try {
400
- await daemonCmd(opts);
401
- }
402
- catch (e) {
403
- handleError(e);
404
- }
405
- });
406
- // schema
407
- const schema = program.command('schema').description('CLI 自省 / 字段字典导出');
408
- schema
409
- .command('commands')
410
- .description('列出所有命令的参数 schema 及输出 schema 标注')
411
- .option('--json', '输出 JSON 信封')
412
- .option('--describe', '输出完整命令 schema 含 outputSchema(agent 自省用)')
413
- .action((opts) => {
414
- try {
415
- schemaCommandsCmd(program, opts);
416
- }
417
- catch (e) {
418
- handleError(e);
419
- }
420
- });
421
- // exec
422
- program
423
- .command('exec')
424
- .description('按 plan-file 顺序执行多条命令(agent 批处理用)')
425
- .requiredOption('--plan-file <path>', 'JSON 计划文件路径')
426
- .option('--json', '输出 JSON 信封')
427
- .action(async (opts) => {
428
- try {
429
- await execCmd(opts);
430
- }
431
- catch (e) {
432
- handleError(e);
433
- }
434
- });
435
- // update
436
- program
437
- .command('update')
438
- .description('检查并自动升级到最新版本(设置 ATLAS_DISABLE_UPDATE=1 禁用)')
439
- .option('--json', '输出 JSON 信封')
440
- .action(async (opts) => {
441
- try {
442
- await updateCmd(opts);
443
- }
444
- catch (e) {
445
- handleError(e);
446
- }
447
- });
448
- // suggest
449
- program
450
- .command('suggest <query...>')
451
- .description('将自然语言查询翻译为候选 atlas 命令(纯规则,不调 LLM)')
452
- .option('--json', '输出 JSON 信封')
453
- .action((tokens, opts) => {
454
- try {
455
- suggestCmd(tokens.join(' '), opts);
456
- }
457
- catch (e) {
458
- handleError(e);
459
- }
460
- });
461
- }
462
- // Version is generated from package.json by scripts/sync-version.mjs (run as
463
- // prebuild). Inlined as a string constant so both `tsc` and bun --compile
464
- // single-file binaries can read it without runtime fs access.
465
- import { ATLAS_VERSION } from './util/version.js';
466
- export function buildProgram() {
467
- const program = new Command();
468
- program
469
- .name('atlas')
470
- .description('Atlas CLI - 斑马云图人力基线管理工具')
471
- .version(ATLAS_VERSION, '-V, --version', '显示版本号')
472
- .option('--json', '以 JSON 信封输出(也可用环境变量 ATLAS_OUTPUT=json)')
473
- .option('--quiet', '静默模式:抑制非信封输出(设置 ATLAS_QUIET=1 也可)')
474
- .option('--describe', '不执行命令,仅输出该命令的参数 schema(agent 自省用)')
475
- .addHelpText('after', `
476
- 退出码:
477
- 0 成功
478
- 1 通用错误 / exec 中某 step 失败
479
- 2 会话过期(需重新登录)
480
- 3 API 返回错误(BanmaApiError)
481
- 4 项目匹配歧义(AmbiguousProject)
482
- 5 项目未找到(ProjectNotFound)
483
- 6 API 限流(RateLimited)
484
- 7 网络错误(NetworkError)
485
- 8 版本更新异常(UpdateError)
486
- 64 配置错误(ConfigError)
487
- 65 输出超限(OutputTooLargeError)
488
- `)
489
- .showHelpAfterError()
490
- .hook('preAction', (thisCommand, actionCommand) => {
491
- const opts = thisCommand.opts();
492
- if (opts.json === true && process.env.ATLAS_OUTPUT === undefined) {
493
- process.env.ATLAS_OUTPUT = 'json';
494
- }
495
- if (opts.quiet === true) {
496
- process.env.ATLAS_QUIET = '1';
497
- }
498
- if (opts.describe === true) {
499
- emitDescribe(actionCommand);
500
- process.exit(0);
501
- }
502
- });
503
- registerAuthCommands(program);
504
- registerProjectCommands(program);
505
- registerBaselineCommands(program);
506
- registerActualCommands(program);
507
- registerCompareCommands(program);
508
- registerUtilityCommands(program);
509
- return program;
510
- }
511
- // 注意:auto-run 入口已移至 bin/atlas.ts
512
- // 此模块仅 export buildProgram / handleError / emitDescribe