pdlab-cli 0.1.2 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +51 -2
  2. package/dist/index.js +418 -117
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -33,6 +33,7 @@ pdlab login --api-url https://your-1836-instance.example.com
33
33
  ```bash
34
34
  pdlab whoami # 看看当前是谁
35
35
  pdlab project list # 列出项目
36
+ pdlab schema # 一次性 JSON 输出全部命令、参数、权限要求、示例和退出码含义——给 agent/脚本用
36
37
  ```
37
38
 
38
39
  ## 命令
@@ -44,20 +45,36 @@ pdlab project list # 列出项目
44
45
  | `pdlab logout` | 清除本地凭证(不撤销服务端的,见下方「撤销凭证」) |
45
46
  | `pdlab whoami` | 查看当前登录账号 |
46
47
  | `pdlab config get` / `set api-url <url>` | 查看/修改本地配置 |
48
+ | `pdlab schema` | JSON 输出完整命令树 + 权限要求 + 示例 + 退出码含义 |
47
49
  | `pdlab project list \| get \| create \| update \| delete` | 项目的增删改查 |
50
+ | `pdlab project join \| leave <id>` | 报名共建 / 撤回申请或退出共建 |
51
+ | `pdlab project member review \| set-role \| remove <projectId> <userId>` | 审核报名、设置协作者角色、移除参与者(仅项目发起人本人) |
52
+ | `pdlab project revenue <id>` | 查看项目的积分分成明细(只读) |
48
53
  | `pdlab post list \| get \| create \| update \| delete` | 内容的增删改查 |
54
+ | `pdlab post review <id> --action approve\|reject` | 审核一条待审内容 |
49
55
 
50
- 任何命令加 `--help` 看具体参数。
56
+ 任何命令加 `--help` 看具体参数——示例已经直接打在 `--help` 末尾,不用等一次报错才能看到。
51
57
 
52
58
  ### 例子
53
59
 
54
60
  ```bash
55
- # 按状态筛选项目
61
+ # 按状态 / 关键词 / 分类标签筛选项目,翻页
56
62
  pdlab project list --status active
63
+ pdlab project list --q "钠电"
64
+ pdlab project list --taxonomy tax_1 --taxonomy tax_2 # 可重复传,多个取交集
65
+ pdlab project list --limit 10 --cursor <上一页返回的 nextCursor>
57
66
 
58
67
  # 创建项目(需要项目创建权限,即「1836发起人」角色)
59
68
  pdlab project create --title "钠电储能原型" --description "第一阶段验证"
60
69
 
70
+ # 报名共建 / 退出
71
+ pdlab project join proj_123
72
+ pdlab project leave proj_123
73
+
74
+ # 项目发起人审核报名、提拔协作者
75
+ pdlab project member review proj_123 user_456 --status approved
76
+ pdlab project member set-role proj_123 user_456 --role maintainer
77
+
61
78
  # 往自己参与的项目发布内容(共建者发布后进待审队列)
62
79
  pdlab post create \
63
80
  --project-id <项目 id> \
@@ -67,8 +84,40 @@ pdlab post create \
67
84
 
68
85
  # 只看某个项目下的内容
69
86
  pdlab post list --project-id <项目 id>
87
+
88
+ # 审核待审内容(需要 content:review 权限)
89
+ pdlab post review post_123 --action approve
90
+ pdlab post review post_123 --action reject --comment "缺少数据来源,请补充引用"
70
91
  ```
71
92
 
93
+ ### 分页
94
+
95
+ `project list` / `post list` 默认单页 30 条(上限 100),响应里的 `nextCursor` 非 `null`
96
+ 就表示还有下一页,把它传给下一次调用的 `--cursor` 继续翻页;`nextCursor` 为 `null`
97
+ 表示已经是最后一页。CLI 不会替你自动拉全量——那样一次调用可能悄悄发出几十次请求。
98
+
99
+ ### 报错与退出码
100
+
101
+ 失败时,一个 JSON 对象会打到 stderr(不是人类可读的长句),方便脚本/agent 解析:
102
+
103
+ ```json
104
+ { "error": "无权限执行此操作", "code": "forbidden", "status": 403 }
105
+ ```
106
+
107
+ 校验类错误还会带上 `fields`(哪个字段、什么问题)和 `hint`(对应命令的示例)。
108
+ 退出码和 `code` 一一对应,`pdlab schema` 的 `exitCodes` 字段里也有同一份:
109
+
110
+ | 退出码 | `code` | 含义 | 建议的处理 |
111
+ |---|---|---|---|
112
+ | 0 | — | 成功 | — |
113
+ | 2 | `validation` | 参数校验失败(本地或服务端) | 按 `fields`/`hint` 改参数重试 |
114
+ | 3 | `unauthenticated` | 未登录或 token 已失效 | 跑 `pdlab login` |
115
+ | 4 | `forbidden` | 无权限 | 不要重试,告知用户 |
116
+ | 5 | `not_found` | 资源不存在 | 检查 id 是否正确 |
117
+ | 6 | `conflict` | 状态冲突(如重复审核) | 不要重试,状态已经变了 |
118
+ | 7 | `network` | 连不上 API 服务器 | 检查 `--api-url`/网络后重试 |
119
+ | 1 | `unknown` / 未分类 | 其他 | 按错误信息人工排查 |
120
+
72
121
  ## 配置
73
122
 
74
123
  配置存在 `~/.pdlab/config.json`(目录 `0700`、文件 `0600`):
package/dist/index.js CHANGED
@@ -35,6 +35,17 @@ var import_open = __toESM(require("open"));
35
35
  var import_fs = require("fs");
36
36
  var import_os = require("os");
37
37
  var import_path = require("path");
38
+
39
+ // src/cli-error.ts
40
+ var CliError = class extends Error {
41
+ constructor(message, code = "unauthenticated") {
42
+ super(message);
43
+ this.code = code;
44
+ }
45
+ code;
46
+ };
47
+
48
+ // src/config.ts
38
49
  var CONFIG_DIR = process.env.PDLAB_CONFIG_DIR ?? (0, import_path.join)((0, import_os.homedir)(), ".pdlab");
39
50
  var CONFIG_FILE = (0, import_path.join)(CONFIG_DIR, "config.json");
40
51
  var DEFAULT_API_URL = "https://1836.online";
@@ -65,28 +76,53 @@ function optionalToken() {
65
76
  function requireToken() {
66
77
  const token = readConfig().token;
67
78
  if (!token) {
68
- console.error("\u5C1A\u672A\u767B\u5F55\u3002\u8BF7\u5148\u8FD0\u884C `pdlab login`\u3002");
69
- process.exit(1);
79
+ throw new CliError("\u5C1A\u672A\u767B\u5F55\u3002\u8BF7\u5148\u8FD0\u884C `pdlab login`\u3002");
70
80
  }
71
81
  return token;
72
82
  }
73
83
 
74
84
  // src/api-client.ts
85
+ function classifyStatus(status) {
86
+ switch (status) {
87
+ case 400:
88
+ case 422:
89
+ return "validation";
90
+ case 401:
91
+ return "unauthenticated";
92
+ case 403:
93
+ return "forbidden";
94
+ case 404:
95
+ return "not_found";
96
+ case 409:
97
+ return "conflict";
98
+ case 0:
99
+ return "network";
100
+ default:
101
+ return "unknown";
102
+ }
103
+ }
75
104
  var ApiRequestError = class extends Error {
76
105
  constructor(message, status, details) {
77
106
  super(message);
78
107
  this.status = status;
79
108
  this.details = details;
109
+ this.code = classifyStatus(status);
80
110
  }
81
111
  status;
82
112
  details;
113
+ code;
83
114
  };
84
115
  async function apiRequest(path, options = {}) {
85
116
  const apiUrl = resolveApiUrl(options.apiUrl);
86
117
  const url = new URL(path.replace(/^\/+/, ""), apiUrl + "/");
87
118
  if (options.query) {
88
119
  for (const [key, value] of Object.entries(options.query)) {
89
- if (value !== void 0) url.searchParams.set(key, value);
120
+ if (value === void 0) continue;
121
+ if (Array.isArray(value)) {
122
+ for (const v of value) url.searchParams.append(key, v);
123
+ } else {
124
+ url.searchParams.set(key, value);
125
+ }
90
126
  }
91
127
  }
92
128
  const headers = {};
@@ -304,41 +340,82 @@ var postReviewSchema = import_zod3.z.object({
304
340
 
305
341
  // src/errors.ts
306
342
  var import_zod4 = require("zod");
307
- function printValidationError(error, examples) {
308
- console.error("\u53C2\u6570\u9519\u8BEF\uFF1A");
343
+ var EXIT_CODES = {
344
+ ok: 0,
345
+ unhandled: 1,
346
+ validation: 2,
347
+ unauthenticated: 3,
348
+ forbidden: 4,
349
+ not_found: 5,
350
+ conflict: 6,
351
+ network: 7,
352
+ unknown: 1
353
+ };
354
+ function fieldsFromZod(error) {
355
+ const fields = {};
309
356
  for (const issue of error.issues) {
310
357
  const path = issue.path.length > 0 ? issue.path.join(".") : "(\u6839)";
311
- console.error(` - ${path}: ${issue.message}`);
358
+ (fields[path] ??= []).push(issue.message);
312
359
  }
313
- printExamples(examples);
360
+ return fields;
314
361
  }
315
- function printExamples(examples) {
316
- if (examples.length === 0) return;
317
- console.error("\n\u793A\u4F8B\uFF1A");
318
- for (const example of examples) {
319
- console.error(` $ ${example.command}`);
320
- if (example.note) console.error(` ${example.note}`);
321
- }
362
+ function fieldsFromApiDetails(details) {
363
+ if (!details || typeof details !== "object") return void 0;
364
+ const flat = details;
365
+ const fields = { ...flat.fieldErrors ?? {} };
366
+ if (flat.formErrors && flat.formErrors.length > 0) fields["(\u6839)"] = flat.formErrors;
367
+ return Object.keys(fields).length > 0 ? fields : void 0;
322
368
  }
323
- function printApiError(error) {
324
- console.error(`\u9519\u8BEF\uFF1A${error.message}`);
325
- if (error.details && typeof error.details === "object") {
326
- const flat = error.details;
327
- for (const [field, messages] of Object.entries(flat.fieldErrors ?? {})) {
328
- for (const message of messages) console.error(` - ${field}: ${message}`);
329
- }
330
- for (const message of flat.formErrors ?? []) console.error(` - ${message}`);
331
- }
369
+ function exampleHint(examples) {
370
+ if (examples.length === 0) return void 0;
371
+ return examples.map((e) => e.command).join(" | ");
332
372
  }
333
- function handleCommandError(error, examples) {
373
+ function handleCommandError(error, examples = []) {
374
+ let structured;
334
375
  if (error instanceof import_zod4.ZodError) {
335
- printValidationError(error, examples);
376
+ structured = {
377
+ error: "\u53C2\u6570\u6821\u9A8C\u5931\u8D25",
378
+ code: "validation",
379
+ status: null,
380
+ fields: fieldsFromZod(error),
381
+ hint: exampleHint(examples)
382
+ };
336
383
  } else if (error instanceof ApiRequestError) {
337
- printApiError(error);
384
+ structured = {
385
+ error: error.message,
386
+ code: error.code,
387
+ status: error.status || null,
388
+ fields: fieldsFromApiDetails(error.details),
389
+ hint: exampleHint(examples)
390
+ };
391
+ } else if (error instanceof CliError) {
392
+ structured = { error: error.message, code: error.code, status: null, hint: "pdlab login" };
338
393
  } else {
339
- console.error(error);
394
+ structured = {
395
+ error: error instanceof Error ? error.message : String(error),
396
+ code: "unhandled",
397
+ status: null
398
+ };
340
399
  }
341
- process.exit(1);
400
+ console.error(JSON.stringify(structured, null, 2));
401
+ process.exit(EXIT_CODES[structured.code] ?? 1);
402
+ }
403
+
404
+ // src/help-meta.ts
405
+ var META = /* @__PURE__ */ new WeakMap();
406
+ function attachMeta(command, meta) {
407
+ META.set(command, meta);
408
+ if (meta.examples && meta.examples.length > 0) {
409
+ command.addHelpText(
410
+ "after",
411
+ "\n\u793A\u4F8B\uFF1A\n" + meta.examples.map((e) => ` $ ${e.command}${e.note ? `
412
+ ${e.note}` : ""}`).join("\n")
413
+ );
414
+ }
415
+ return command;
416
+ }
417
+ function getMeta(command) {
418
+ return META.get(command) ?? {};
342
419
  }
343
420
 
344
421
  // src/commands/options.ts
@@ -347,15 +424,50 @@ function withApiUrl(cmd) {
347
424
  }
348
425
 
349
426
  // src/commands/project.ts
427
+ var STATUS_LIST = PROJECT_STATUS_VALUES.join(" | ");
350
428
  var CREATE_EXAMPLES = [
351
429
  {
352
430
  command: 'pdlab project create --title "\u94A0\u7535\u56DE\u6536\u5DE5\u827A\u4F18\u5316" --description "..." --status planning --taxonomy-ids tax_1,tax_2',
353
- note: "title \u5FC5\u586B\uFF0C\u5176\u4F59\u5B57\u6BB5\u53EF\u7701\u7565"
431
+ note: "title \u5FC5\u586B\uFF0C\u5176\u4F59\u5B57\u6BB5\u53EF\u7701\u7565\uFF1B\u9700\u8981\u300C1836\u53D1\u8D77\u4EBA\u300D\u89D2\u8272\uFF08project:create \u6743\u9650\uFF09"
354
432
  }
355
433
  ];
356
434
  var UPDATE_EXAMPLES = [
357
435
  { command: "pdlab project update proj_123 --progress 60 --status active", note: "\u53EA\u4F20\u8981\u6539\u7684\u5B57\u6BB5" }
358
436
  ];
437
+ var LIST_EXAMPLES = [
438
+ { command: "pdlab project list --status active", note: "\u6309\u72B6\u6001\u7B5B\u9009" },
439
+ { command: 'pdlab project list --q "\u94A0\u7535"', note: "\u6309\u6807\u9898\u5173\u952E\u8BCD\u641C\u7D22" },
440
+ { command: "pdlab project list --taxonomy tax_1 --taxonomy tax_2", note: "\u53EF\u91CD\u590D\u4F20\uFF0C\u591A\u4E2A\u6807\u7B7E\u53D6\u4EA4\u96C6" },
441
+ { command: "pdlab project list --limit 10 --cursor <\u4E0A\u4E00\u9875\u8FD4\u56DE\u7684 nextCursor>", note: "\u7FFB\u9875\uFF1BnextCursor \u4E3A null \u8868\u793A\u6CA1\u6709\u4E0B\u4E00\u9875\u4E86" }
442
+ ];
443
+ var GET_EXAMPLES = [{ command: "pdlab project get proj_123" }];
444
+ var DELETE_EXAMPLES = [
445
+ { command: "pdlab project delete proj_123", note: "\u9700\u8981\u662F\u9879\u76EE\u53D1\u8D77\u4EBA\uFF0C\u4E14\u9879\u76EE\u4E0B\u6CA1\u6709\u5185\u5BB9/\u79EF\u5206\u8BB0\u5F55" }
446
+ ];
447
+ var JOIN_EXAMPLES = [
448
+ { command: "pdlab project join proj_123", note: "\u62A5\u540D\u5171\u5EFA\uFF0C\u8FDB\u5165\u5F85\u5BA1\u6838\uFF1B\u91CD\u590D\u8C03\u7528\u662F\u5E42\u7B49\u7684" }
449
+ ];
450
+ var LEAVE_EXAMPLES = [
451
+ { command: "pdlab project leave proj_123", note: "\u64A4\u56DE\u62A5\u540D\u7533\u8BF7\uFF0C\u6216\u9000\u51FA\u5DF2\u901A\u8FC7\u5BA1\u6838\u7684\u5171\u5EFA" }
452
+ ];
453
+ var MEMBER_REVIEW_EXAMPLES = [
454
+ {
455
+ command: "pdlab project member review proj_123 user_456 --status approved",
456
+ note: "\u5BA1\u6838\u4E00\u6761 pending \u7684\u62A5\u540D\u7533\u8BF7\uFF1B\u53EA\u6709\u9879\u76EE\u53D1\u8D77\u4EBA\u672C\u4EBA\u80FD\u8C03\u7528"
457
+ }
458
+ ];
459
+ var MEMBER_SET_ROLE_EXAMPLES = [
460
+ {
461
+ command: "pdlab project member set-role proj_123 user_456 --role maintainer",
462
+ note: "\u4EC5\u5BF9\u5DF2\u901A\u8FC7\u5BA1\u6838\uFF08approved\uFF09\u7684\u53C2\u4E0E\u8005\u751F\u6548\uFF1B\u53EA\u6709\u9879\u76EE\u53D1\u8D77\u4EBA\u672C\u4EBA\u80FD\u8C03\u7528"
463
+ }
464
+ ];
465
+ var MEMBER_REMOVE_EXAMPLES = [
466
+ { command: "pdlab project member remove proj_123 user_456", note: "\u53EA\u6709\u9879\u76EE\u53D1\u8D77\u4EBA\u672C\u4EBA\u80FD\u8C03\u7528" }
467
+ ];
468
+ var REVENUE_EXAMPLES = [
469
+ { command: "pdlab project revenue proj_123", note: "\u53EA\u8BFB\uFF0C\u9700\u8981 data:view_stats \u6743\u9650" }
470
+ ];
359
471
  function toBody(opts) {
360
472
  const body = {};
361
473
  if (opts.title !== void 0) body.title = opts.title;
@@ -370,26 +482,43 @@ function toBody(opts) {
370
482
  return body;
371
483
  }
372
484
  function withProjectFlags(cmd) {
373
- return withApiUrl(cmd).option("--title <title>", "\u9879\u76EE\u6807\u9898").option("--description <description>", "\u9879\u76EE\u63CF\u8FF0").option("--cover-image <url>", "\u5C01\u9762\u56FE http/https \u94FE\u63A5").option("--commercial-type <type>", "\u5546\u4E1A\u5C5E\u6027").option("--status <status>", "planning | active | review | completed").option("--progress <percent>", "\u8FDB\u5EA6 0-100").option("--taxonomy-ids <ids>", "\u9017\u53F7\u5206\u9694\u7684\u5206\u7C7B\u6807\u7B7E id \u5217\u8868");
485
+ return withApiUrl(cmd).option("--title <title>", "\u9879\u76EE\u6807\u9898\uFF08\u5FC5\u586B\uFF0C1-200 \u5B57\uFF09").option("--description <description>", "\u9879\u76EE\u63CF\u8FF0\uFF08\u9009\u586B\uFF0C\u6700\u591A 20000 \u5B57\uFF09").option("--cover-image <url>", "\u5C01\u9762\u56FE http/https \u94FE\u63A5\uFF08\u9009\u586B\uFF09").option("--commercial-type <type>", "\u5546\u4E1A\u5C5E\u6027\uFF08\u9009\u586B\uFF09").option("--status <status>", `\u9879\u76EE\u72B6\u6001\uFF1A${STATUS_LIST}\uFF08\u9009\u586B\uFF09`).option("--progress <percent>", "\u8FDB\u5EA6\uFF0C\u6574\u6570 0-100\uFF08\u9009\u586B\uFF09").option("--taxonomy-ids <ids>", "\u9017\u53F7\u5206\u9694\u7684\u5206\u7C7B\u6807\u7B7E id \u5217\u8868\uFF0C\u5168\u91CF\u66FF\u6362\uFF08\u9009\u586B\uFF09");
486
+ }
487
+ function collect(value, previous) {
488
+ return previous.concat([value]);
374
489
  }
375
490
  function registerProjectCommand(program2) {
376
- const project = program2.command("project").description("\u9879\u76EE\u7684\u589E\u5220\u6539\u67E5");
377
- withApiUrl(project.command("list").description("\u5217\u51FA\u9879\u76EE")).option("--status <status>", "\u6309\u72B6\u6001\u7B5B\u9009\uFF1Aplanning | active | review | completed").action(async (opts) => {
378
- const data = await apiRequest("/api/projects", {
379
- query: { status: opts.status },
380
- token: optionalToken(),
381
- apiUrl: opts.apiUrl
382
- });
383
- console.log(JSON.stringify(data, null, 2));
384
- });
385
- withApiUrl(project.command("get <id>").description("\u67E5\u770B\u9879\u76EE\u8BE6\u60C5")).action(
386
- async (id, opts) => {
387
- const data = await apiRequest(`/api/projects/${id}`, { token: optionalToken(), apiUrl: opts.apiUrl });
388
- console.log(JSON.stringify(data, null, 2));
389
- }
491
+ const project = program2.command("project").description("\u9879\u76EE\u7684\u589E\u5220\u6539\u67E5\u3001\u62A5\u540D\u5171\u5EFA\u3001\u6210\u5458\u7BA1\u7406\u3001\u6536\u76CA\u67E5\u8BE2");
492
+ attachMeta(
493
+ withApiUrl(project.command("list").description("\u5217\u51FA\u9879\u76EE\uFF08\u516C\u5F00\u53EA\u8BFB\uFF0C\u4E0D\u767B\u5F55\u4E5F\u80FD\u7528\uFF09")).option("--status <status>", `\u6309\u72B6\u6001\u7B5B\u9009\uFF1A${STATUS_LIST}`).option("--q <keyword>", "\u6309\u6807\u9898\u5173\u952E\u8BCD\u641C\u7D22").option("--taxonomy <id>", "\u6309\u5206\u7C7B\u6807\u7B7E id \u7B5B\u9009\uFF0C\u53EF\u91CD\u590D\u4F20\uFF0C\u591A\u4E2A\u53D6\u4EA4\u96C6", collect, []).option("--limit <n>", "\u5355\u9875\u6570\u91CF\uFF0C\u9ED8\u8BA4 30\uFF0C\u4E0A\u9650 100").option("--cursor <token>", "\u7FFB\u9875\u6E38\u6807\uFF0C\u53D6\u4E0A\u4E00\u9875\u54CD\u5E94\u91CC\u7684 nextCursor").action(
494
+ async (opts) => {
495
+ const data = await apiRequest("/api/projects", {
496
+ query: {
497
+ status: opts.status,
498
+ q: opts.q,
499
+ taxonomy: opts.taxonomy.length > 0 ? opts.taxonomy : void 0,
500
+ limit: opts.limit,
501
+ cursor: opts.cursor
502
+ },
503
+ token: optionalToken(),
504
+ apiUrl: opts.apiUrl
505
+ });
506
+ console.log(JSON.stringify(data, null, 2));
507
+ }
508
+ ),
509
+ { examples: LIST_EXAMPLES }
390
510
  );
391
- withProjectFlags(project.command("create").description("\u521B\u5EFA\u9879\u76EE\uFF08\u9700\u8981\u9879\u76EE\u521B\u5EFA\u6743\u9650\uFF09")).action(
392
- async (opts) => {
511
+ attachMeta(
512
+ withApiUrl(project.command("get <id>").description("\u67E5\u770B\u9879\u76EE\u8BE6\u60C5\uFF08\u516C\u5F00\u53EA\u8BFB\uFF0C\u4E0D\u767B\u5F55\u4E5F\u80FD\u7528\uFF09")).action(
513
+ async (id, opts) => {
514
+ const data = await apiRequest(`/api/projects/${id}`, { token: optionalToken(), apiUrl: opts.apiUrl });
515
+ console.log(JSON.stringify(data, null, 2));
516
+ }
517
+ ),
518
+ { examples: GET_EXAMPLES }
519
+ );
520
+ attachMeta(
521
+ withProjectFlags(project.command("create").description("\u521B\u5EFA\u9879\u76EE")).action(async (opts) => {
393
522
  const token = requireToken();
394
523
  try {
395
524
  const body = projectCreateSchema.parse(toBody(opts));
@@ -398,99 +527,275 @@ function registerProjectCommand(program2) {
398
527
  } catch (error) {
399
528
  handleCommandError(error, CREATE_EXAMPLES);
400
529
  }
530
+ }),
531
+ { permission: "project:create\uFF081836\u53D1\u8D77\u4EBA\u89D2\u8272\uFF09", examples: CREATE_EXAMPLES }
532
+ );
533
+ attachMeta(
534
+ withProjectFlags(project.command("update <id>").description("\u66F4\u65B0\u9879\u76EE")).action(
535
+ async (id, opts) => {
536
+ const token = requireToken();
537
+ try {
538
+ const body = projectUpdateSchema.parse(toBody(opts));
539
+ const data = await apiRequest(`/api/projects/${id}`, { method: "PATCH", body, token, apiUrl: opts.apiUrl });
540
+ console.log(JSON.stringify(data, null, 2));
541
+ } catch (error) {
542
+ handleCommandError(error, UPDATE_EXAMPLES);
543
+ }
544
+ }
545
+ ),
546
+ {
547
+ permission: "project:update_progress\uFF08\u53D1\u8D77\u4EBA\uFF09\u6216 project:join\uFF08\u534F\u4F5C\u8005\uFF0C\u4EC5\u80FD\u6539 progress/status/description\uFF09",
548
+ examples: UPDATE_EXAMPLES
401
549
  }
402
550
  );
403
- withProjectFlags(project.command("update <id>").description("\u66F4\u65B0\u9879\u76EE")).action(
404
- async (id, opts) => {
551
+ attachMeta(
552
+ withApiUrl(
553
+ project.command("delete <id>").description("\u5220\u9664\u9879\u76EE\uFF08\u9700\u8981\u662F\u9879\u76EE\u53D1\u8D77\u4EBA\uFF0C\u4E14\u9879\u76EE\u4E0B\u6CA1\u6709\u5185\u5BB9/\u79EF\u5206\u8BB0\u5F55\uFF09")
554
+ ).action(async (id, opts) => {
405
555
  const token = requireToken();
406
- try {
407
- const body = projectUpdateSchema.parse(toBody(opts));
408
- const data = await apiRequest(`/api/projects/${id}`, { method: "PATCH", body, token, apiUrl: opts.apiUrl });
556
+ const data = await apiRequest(`/api/projects/${id}`, { method: "DELETE", token, apiUrl: opts.apiUrl });
557
+ console.log(JSON.stringify(data, null, 2));
558
+ }),
559
+ { permission: "project:create\uFF081836\u53D1\u8D77\u4EBA\u89D2\u8272\uFF09\uFF0C\u4E14\u5FC5\u987B\u662F\u8BE5\u9879\u76EE\u53D1\u8D77\u4EBA\u672C\u4EBA", examples: DELETE_EXAMPLES }
560
+ );
561
+ attachMeta(
562
+ withApiUrl(project.command("join <id>").description("\u62A5\u540D\u5171\u5EFA\u8BE5\u9879\u76EE\uFF0C\u8FDB\u5165\u5F85\u5BA1\u6838")).action(
563
+ async (id, opts) => {
564
+ const token = requireToken();
565
+ const data = await apiRequest(`/api/projects/${id}/join`, { method: "POST", token, apiUrl: opts.apiUrl });
409
566
  console.log(JSON.stringify(data, null, 2));
410
- } catch (error) {
411
- handleCommandError(error, UPDATE_EXAMPLES);
412
567
  }
413
- }
568
+ ),
569
+ { permission: "project:join\uFF081836\u5171\u5EFA\u8005\u7B49\u89D2\u8272\uFF09", examples: JOIN_EXAMPLES }
570
+ );
571
+ attachMeta(
572
+ withApiUrl(project.command("leave <id>").description("\u64A4\u56DE\u62A5\u540D\u7533\u8BF7\uFF0C\u6216\u9000\u51FA\u5DF2\u901A\u8FC7\u5BA1\u6838\u7684\u5171\u5EFA")).action(
573
+ async (id, opts) => {
574
+ const token = requireToken();
575
+ const data = await apiRequest(`/api/projects/${id}/join`, { method: "DELETE", token, apiUrl: opts.apiUrl });
576
+ console.log(JSON.stringify(data, null, 2));
577
+ }
578
+ ),
579
+ { permission: "project:join\uFF081836\u5171\u5EFA\u8005\u7B49\u89D2\u8272\uFF09", examples: LEAVE_EXAMPLES }
580
+ );
581
+ const member = project.command("member").description("\u9879\u76EE\u53C2\u4E0E\u8005\u7684\u5BA1\u6838 / \u89D2\u8272 / \u79FB\u9664\uFF08\u4EC5\u9879\u76EE\u53D1\u8D77\u4EBA\u672C\u4EBA\u53EF\u7528\uFF09");
582
+ attachMeta(
583
+ withApiUrl(member.command("review <projectId> <userId>").description("\u5BA1\u6838\u4E00\u6761 pending \u7684\u62A5\u540D\u7533\u8BF7")).requiredOption("--status <status>", "approved | rejected").action(async (projectId, userId, opts) => {
584
+ const token = requireToken();
585
+ const data = await apiRequest(`/api/projects/${projectId}/members/${userId}`, {
586
+ method: "PATCH",
587
+ body: { status: opts.status },
588
+ token,
589
+ apiUrl: opts.apiUrl
590
+ });
591
+ console.log(JSON.stringify(data, null, 2));
592
+ }),
593
+ { permission: "project:update_progress\uFF0C\u4E14\u5FC5\u987B\u662F\u8BE5\u9879\u76EE\u53D1\u8D77\u4EBA\u672C\u4EBA", examples: MEMBER_REVIEW_EXAMPLES }
594
+ );
595
+ attachMeta(
596
+ withApiUrl(member.command("set-role <projectId> <userId>").description("\u8BBE\u7F6E\u5DF2\u901A\u8FC7\u5BA1\u6838\u7684\u53C2\u4E0E\u8005\u7684\u9879\u76EE\u5185\u89D2\u8272")).requiredOption("--role <role>", "member | maintainer").action(async (projectId, userId, opts) => {
597
+ const token = requireToken();
598
+ const data = await apiRequest(`/api/projects/${projectId}/members/${userId}`, {
599
+ method: "PATCH",
600
+ body: { role: opts.role },
601
+ token,
602
+ apiUrl: opts.apiUrl
603
+ });
604
+ console.log(JSON.stringify(data, null, 2));
605
+ }),
606
+ { permission: "project:update_progress\uFF0C\u4E14\u5FC5\u987B\u662F\u8BE5\u9879\u76EE\u53D1\u8D77\u4EBA\u672C\u4EBA", examples: MEMBER_SET_ROLE_EXAMPLES }
607
+ );
608
+ attachMeta(
609
+ withApiUrl(member.command("remove <projectId> <userId>").description("\u79FB\u9664\u4E00\u4E2A\u53C2\u4E0E\u8005")).action(
610
+ async (projectId, userId, opts) => {
611
+ const token = requireToken();
612
+ const data = await apiRequest(`/api/projects/${projectId}/members/${userId}`, {
613
+ method: "DELETE",
614
+ token,
615
+ apiUrl: opts.apiUrl
616
+ });
617
+ console.log(JSON.stringify(data, null, 2));
618
+ }
619
+ ),
620
+ { permission: "project:update_progress\uFF0C\u4E14\u5FC5\u987B\u662F\u8BE5\u9879\u76EE\u53D1\u8D77\u4EBA\u672C\u4EBA", examples: MEMBER_REMOVE_EXAMPLES }
621
+ );
622
+ attachMeta(
623
+ withApiUrl(project.command("revenue <id>").description("\u67E5\u770B\u9879\u76EE\u7684\u79EF\u5206\u5206\u6210\u660E\u7EC6\uFF08\u53EA\u8BFB\uFF09")).action(
624
+ async (id, opts) => {
625
+ const token = requireToken();
626
+ const data = await apiRequest(`/api/projects/${id}/revenue-breakdown`, { token, apiUrl: opts.apiUrl });
627
+ console.log(JSON.stringify(data, null, 2));
628
+ }
629
+ ),
630
+ { permission: "data:view_stats", examples: REVENUE_EXAMPLES }
414
631
  );
415
- withApiUrl(
416
- project.command("delete <id>").description("\u5220\u9664\u9879\u76EE\uFF08\u9700\u8981\u662F\u9879\u76EE\u53D1\u8D77\u4EBA\uFF0C\u4E14\u9879\u76EE\u4E0B\u6CA1\u6709\u5185\u5BB9/\u79EF\u5206\u8BB0\u5F55\uFF09")
417
- ).action(async (id, opts) => {
418
- const token = requireToken();
419
- const data = await apiRequest(`/api/projects/${id}`, { method: "DELETE", token, apiUrl: opts.apiUrl });
420
- console.log(JSON.stringify(data, null, 2));
421
- });
422
632
  }
423
633
 
424
634
  // src/commands/post.ts
425
635
  var CREATE_EXAMPLES2 = [
426
636
  {
427
637
  command: 'pdlab post create --title "\u7B2C\u4E00\u5468\u8FDB\u5C55" --body "..." --project-id proj_123 --media-links https://example.com/a.png',
428
- note: "title / body / project-id \u5FC5\u586B"
638
+ note: "title / body / project-id \u5FC5\u586B\uFF1B\u9700\u8981\u662F\u9879\u76EE\u53D1\u8D77\u4EBA\u6216\u5DF2\u62A5\u540D\u5171\u5EFA\u8BE5\u9879\u76EE"
639
+ }
640
+ ];
641
+ var UPDATE_EXAMPLES2 = [
642
+ { command: 'pdlab post update post_123 --title "..." --body "..."', note: "title \u548C body \u90FD\u5FC5\u987B\u4F20" }
643
+ ];
644
+ var LIST_EXAMPLES2 = [
645
+ { command: "pdlab post list --project-id proj_123", note: "\u53EA\u770B\u67D0\u4E2A\u9879\u76EE\u4E0B\u7684\u5185\u5BB9" },
646
+ { command: "pdlab post list --status pending", note: "\u67E5\u770B\u5F85\u5BA1\u6838\u5185\u5BB9\uFF0C\u9700\u8981 content:review \u6743\u9650\uFF0C\u5426\u5219 403" },
647
+ { command: "pdlab post list --limit 10 --cursor <\u4E0A\u4E00\u9875\u8FD4\u56DE\u7684 nextCursor>", note: "\u7FFB\u9875\uFF1BnextCursor \u4E3A null \u8868\u793A\u6CA1\u6709\u4E0B\u4E00\u9875\u4E86" }
648
+ ];
649
+ var GET_EXAMPLES2 = [{ command: "pdlab post get post_123" }];
650
+ var DELETE_EXAMPLES2 = [
651
+ { command: "pdlab post delete post_123", note: "\u4F5C\u8005\u672C\u4EBA\uFF0C\u6216\u6709\u5220\u9664\u4EFB\u610F\u5185\u5BB9\u7684\u6743\u9650" }
652
+ ];
653
+ var REVIEW_EXAMPLES = [
654
+ {
655
+ command: "pdlab post review post_123 --action approve",
656
+ note: "\u901A\u8FC7\u5BA1\u6838\uFF1B\u4F5C\u8005\u4E0D\u80FD\u5BA1\u81EA\u5DF1\u53D1\u5E03\u7684\u5185\u5BB9\uFF08403\uFF09\uFF0C\u53EA\u80FD\u5BA1 pending \u72B6\u6001\u7684\u5185\u5BB9\uFF08\u975E pending \u4F1A 409\uFF09"
657
+ },
658
+ {
659
+ command: 'pdlab post review post_123 --action reject --comment "\u7F3A\u5C11\u6570\u636E\u6765\u6E90\uFF0C\u8BF7\u8865\u5145\u5F15\u7528"',
660
+ note: "\u9A73\u56DE\u65F6\u5FC5\u987B\u586B\u5199 --comment\uFF0C\u5426\u5219 400"
429
661
  }
430
662
  ];
431
- var UPDATE_EXAMPLES2 = [{ command: 'pdlab post update post_123 --title "..." --body "..."', note: "title \u548C body \u90FD\u5FC5\u987B\u4F20" }];
432
663
  function toMediaLinks(value) {
433
664
  if (value === void 0) return void 0;
434
665
  return value.split(",").map((url) => url.trim()).filter(Boolean);
435
666
  }
436
667
  function registerPostCommand(program2) {
437
- const post = program2.command("post").description("\u5185\u5BB9\uFF08\u5E16\u5B50\uFF09\u7684\u589E\u5220\u6539\u67E5");
438
- withApiUrl(post.command("list").description("\u5217\u51FA\u5185\u5BB9")).option("--project-id <id>", "\u6309\u9879\u76EE\u7B5B\u9009").option("--status <status>", "pending | approved | rejected\uFF08\u67E5\u770B\u975E approved \u9700\u8981\u5BA1\u6838\u6743\u9650\uFF09").action(async (opts) => {
439
- const data = await apiRequest("/api/posts", {
440
- query: { projectId: opts.projectId, status: opts.status },
441
- token: optionalToken(),
442
- apiUrl: opts.apiUrl
443
- });
444
- console.log(JSON.stringify(data, null, 2));
445
- });
446
- withApiUrl(post.command("get <id>").description("\u67E5\u770B\u5185\u5BB9\u8BE6\u60C5")).action(
447
- async (id, opts) => {
448
- const data = await apiRequest(`/api/posts/${id}`, { token: optionalToken(), apiUrl: opts.apiUrl });
449
- console.log(JSON.stringify(data, null, 2));
450
- }
451
- );
452
- withApiUrl(post.command("create").description("\u53D1\u5E03\u5185\u5BB9\uFF08\u9700\u8981\u5185\u5BB9\u521B\u5EFA\u6743\u9650\uFF0C\u4E14\u662F\u9879\u76EE\u53D1\u8D77\u4EBA\u6216\u5DF2\u62A5\u540D\u5171\u5EFA\uFF09")).option("--title <title>", "\u5185\u5BB9\u6807\u9898").option("--body <body>", "\u5185\u5BB9\u6B63\u6587").option("--project-id <id>", "\u6240\u5C5E\u9879\u76EE id").option("--media-links <urls>", "\u9017\u53F7\u5206\u9694\u7684\u5A92\u4F53\u94FE\u63A5").action(async (opts) => {
453
- const token = requireToken();
454
- try {
455
- const body = postCreateSchema.parse({
456
- title: opts.title,
457
- body: opts.body,
458
- projectId: opts.projectId,
459
- mediaLinks: toMediaLinks(opts.mediaLinks)
668
+ const post = program2.command("post").description("\u5185\u5BB9\uFF08\u5E16\u5B50\uFF09\u7684\u589E\u5220\u6539\u67E5\u4E0E\u5BA1\u6838");
669
+ attachMeta(
670
+ withApiUrl(post.command("list").description("\u5217\u51FA\u5185\u5BB9\uFF08approved \u5185\u5BB9\u516C\u5F00\u53EA\u8BFB\uFF0C\u4E0D\u767B\u5F55\u4E5F\u80FD\u7528\uFF09")).option("--project-id <id>", "\u6309\u9879\u76EE\u7B5B\u9009").option("--status <status>", "pending | approved | rejected\uFF08\u67E5\u770B\u975E approved \u9700\u8981\u5BA1\u6838\u6743\u9650\uFF09").option("--limit <n>", "\u5355\u9875\u6570\u91CF\uFF0C\u9ED8\u8BA4 30\uFF0C\u4E0A\u9650 100").option("--cursor <token>", "\u7FFB\u9875\u6E38\u6807\uFF0C\u53D6\u4E0A\u4E00\u9875\u54CD\u5E94\u91CC\u7684 nextCursor").action(async (opts) => {
671
+ const data = await apiRequest("/api/posts", {
672
+ query: { projectId: opts.projectId, status: opts.status, limit: opts.limit, cursor: opts.cursor },
673
+ token: optionalToken(),
674
+ apiUrl: opts.apiUrl
460
675
  });
461
- const data = await apiRequest("/api/posts", { method: "POST", body, token, apiUrl: opts.apiUrl });
462
676
  console.log(JSON.stringify(data, null, 2));
463
- } catch (error) {
464
- handleCommandError(error, CREATE_EXAMPLES2);
465
- }
466
- });
467
- withApiUrl(post.command("update <id>").description("\u7F16\u8F91\u5185\u5BB9\uFF08\u4F5C\u8005\u672C\u4EBA\uFF0C\u6216\u6709\u7F16\u8F91\u4EFB\u610F\u5185\u5BB9\u7684\u6743\u9650\uFF09")).option("--title <title>", "\u5185\u5BB9\u6807\u9898").option("--body <body>", "\u5185\u5BB9\u6B63\u6587").option("--media-links <urls>", "\u9017\u53F7\u5206\u9694\u7684\u5A92\u4F53\u94FE\u63A5").action(async (id, opts) => {
468
- const token = requireToken();
469
- try {
470
- const body = postUpdateSchema.parse({
471
- title: opts.title,
472
- body: opts.body,
473
- mediaLinks: toMediaLinks(opts.mediaLinks)
474
- });
475
- const data = await apiRequest(`/api/posts/${id}`, { method: "PATCH", body, token, apiUrl: opts.apiUrl });
476
- console.log(JSON.stringify(data, null, 2));
477
- } catch (error) {
478
- handleCommandError(error, UPDATE_EXAMPLES2);
479
- }
480
- });
481
- withApiUrl(post.command("delete <id>").description("\u5220\u9664\u5185\u5BB9\uFF08\u4F5C\u8005\u672C\u4EBA\uFF0C\u6216\u6709\u5220\u9664\u4EFB\u610F\u5185\u5BB9\u7684\u6743\u9650\uFF09")).action(
482
- async (id, opts) => {
677
+ }),
678
+ { examples: LIST_EXAMPLES2 }
679
+ );
680
+ attachMeta(
681
+ withApiUrl(post.command("get <id>").description("\u67E5\u770B\u5185\u5BB9\u8BE6\u60C5")).action(
682
+ async (id, opts) => {
683
+ const data = await apiRequest(`/api/posts/${id}`, { token: optionalToken(), apiUrl: opts.apiUrl });
684
+ console.log(JSON.stringify(data, null, 2));
685
+ }
686
+ ),
687
+ { examples: GET_EXAMPLES2 }
688
+ );
689
+ attachMeta(
690
+ withApiUrl(post.command("create").description("\u53D1\u5E03\u5185\u5BB9")).option("--title <title>", "\u5185\u5BB9\u6807\u9898\uFF08\u5FC5\u586B\uFF09").option("--body <body>", "\u5185\u5BB9\u6B63\u6587\uFF08\u5FC5\u586B\uFF09").option("--project-id <id>", "\u6240\u5C5E\u9879\u76EE id\uFF08\u5FC5\u586B\uFF09").option("--media-links <urls>", "\u9017\u53F7\u5206\u9694\u7684\u5A92\u4F53\u94FE\u63A5\uFF08\u9009\u586B\uFF0C\u6700\u591A 20 \u6761\uFF0Chttp/https\uFF09").action(async (opts) => {
483
691
  const token = requireToken();
484
- const data = await apiRequest(`/api/posts/${id}`, { method: "DELETE", token, apiUrl: opts.apiUrl });
485
- console.log(JSON.stringify(data, null, 2));
486
- }
692
+ try {
693
+ const body = postCreateSchema.parse({
694
+ title: opts.title,
695
+ body: opts.body,
696
+ projectId: opts.projectId,
697
+ mediaLinks: toMediaLinks(opts.mediaLinks)
698
+ });
699
+ const data = await apiRequest("/api/posts", { method: "POST", body, token, apiUrl: opts.apiUrl });
700
+ console.log(JSON.stringify(data, null, 2));
701
+ } catch (error) {
702
+ handleCommandError(error, CREATE_EXAMPLES2);
703
+ }
704
+ }),
705
+ { permission: "content:create\uFF0C\u4E14\u662F\u9879\u76EE\u53D1\u8D77\u4EBA\u6216\u5DF2\u62A5\u540D\u5171\u5EFA\u8BE5\u9879\u76EE", examples: CREATE_EXAMPLES2 }
487
706
  );
707
+ attachMeta(
708
+ withApiUrl(post.command("update <id>").description("\u7F16\u8F91\u5185\u5BB9\uFF08\u4F5C\u8005\u672C\u4EBA\uFF0C\u6216\u6709\u7F16\u8F91\u4EFB\u610F\u5185\u5BB9\u7684\u6743\u9650\uFF09")).option("--title <title>", "\u5185\u5BB9\u6807\u9898\uFF08\u5FC5\u586B\uFF09").option("--body <body>", "\u5185\u5BB9\u6B63\u6587\uFF08\u5FC5\u586B\uFF09").option("--media-links <urls>", "\u9017\u53F7\u5206\u9694\u7684\u5A92\u4F53\u94FE\u63A5\uFF08\u9009\u586B\uFF0C\u6700\u591A 20 \u6761\uFF0Chttp/https\uFF09").action(async (id, opts) => {
709
+ const token = requireToken();
710
+ try {
711
+ const body = postUpdateSchema.parse({
712
+ title: opts.title,
713
+ body: opts.body,
714
+ mediaLinks: toMediaLinks(opts.mediaLinks)
715
+ });
716
+ const data = await apiRequest(`/api/posts/${id}`, { method: "PATCH", body, token, apiUrl: opts.apiUrl });
717
+ console.log(JSON.stringify(data, null, 2));
718
+ } catch (error) {
719
+ handleCommandError(error, UPDATE_EXAMPLES2);
720
+ }
721
+ }),
722
+ { examples: UPDATE_EXAMPLES2 }
723
+ );
724
+ attachMeta(
725
+ withApiUrl(post.command("delete <id>").description("\u5220\u9664\u5185\u5BB9\uFF08\u4F5C\u8005\u672C\u4EBA\uFF0C\u6216\u6709\u5220\u9664\u4EFB\u610F\u5185\u5BB9\u7684\u6743\u9650\uFF09")).action(
726
+ async (id, opts) => {
727
+ const token = requireToken();
728
+ const data = await apiRequest(`/api/posts/${id}`, { method: "DELETE", token, apiUrl: opts.apiUrl });
729
+ console.log(JSON.stringify(data, null, 2));
730
+ }
731
+ ),
732
+ { examples: DELETE_EXAMPLES2 }
733
+ );
734
+ attachMeta(
735
+ withApiUrl(post.command("review <id>").description("\u5BA1\u6838\u4E00\u6761\u5F85\u5BA1\u5185\u5BB9\uFF08approve / reject\uFF09")).requiredOption("--action <action>", "approve | reject").option("--comment <text>", "\u5BA1\u6838\u610F\u89C1\uFF0C\u6700\u591A 500 \u5B57\uFF08reject \u65F6\u5FC5\u586B\uFF09").action(async (id, opts) => {
736
+ const token = requireToken();
737
+ try {
738
+ const body = postReviewSchema.parse({ action: opts.action, reviewComment: opts.comment });
739
+ const data = await apiRequest(`/api/posts/${id}/review`, { method: "POST", body, token, apiUrl: opts.apiUrl });
740
+ console.log(JSON.stringify(data, null, 2));
741
+ } catch (error) {
742
+ handleCommandError(error, REVIEW_EXAMPLES);
743
+ }
744
+ }),
745
+ { permission: "content:review\uFF0C\u4E14\u4E0D\u80FD\u5BA1\u6838\u81EA\u5DF1\u53D1\u5E03\u7684\u5185\u5BB9", examples: REVIEW_EXAMPLES }
746
+ );
747
+ }
748
+
749
+ // src/commands/schema.ts
750
+ var CODE_DESCRIPTION = {
751
+ ok: "\u6210\u529F",
752
+ unhandled: "\u672A\u5206\u7C7B\u9519\u8BEF",
753
+ validation: "\u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF08\u672C\u5730\u6216\u670D\u52A1\u7AEF\uFF09",
754
+ unauthenticated: "\u672A\u767B\u5F55\u6216 token \u5DF2\u5931\u6548\uFF0C\u9700\u8981 pdlab login",
755
+ forbidden: "\u65E0\u6743\u9650\uFF08\u670D\u52A1\u7AEF 403\uFF09",
756
+ not_found: "\u8D44\u6E90\u4E0D\u5B58\u5728\uFF08\u670D\u52A1\u7AEF 404\uFF09",
757
+ conflict: "\u72B6\u6001\u51B2\u7A81\uFF0C\u4F8B\u5982\u91CD\u590D\u5BA1\u6838\uFF08\u670D\u52A1\u7AEF 409\uFF09",
758
+ network: "\u8FDE\u4E0D\u4E0A API \u670D\u52A1\u5668",
759
+ unknown: "\u672A\u5206\u7C7B\u7684\u670D\u52A1\u7AEF\u9519\u8BEF"
760
+ };
761
+ function exitCodeTable() {
762
+ const table = {};
763
+ for (const [code, exitStatus] of Object.entries(EXIT_CODES)) {
764
+ if (table[exitStatus] === void 0) table[exitStatus] = CODE_DESCRIPTION[code];
765
+ }
766
+ return table;
767
+ }
768
+ function serialize(command) {
769
+ const meta = getMeta(command);
770
+ return {
771
+ name: command.name(),
772
+ description: command.description(),
773
+ permission: meta.permission,
774
+ arguments: command.registeredArguments.map((a) => ({ name: a.name(), required: a.required })),
775
+ options: command.options.map((o) => ({ flags: o.flags, description: o.description, required: o.mandatory })),
776
+ examples: meta.examples ?? [],
777
+ subcommands: command.commands.map(serialize)
778
+ };
779
+ }
780
+ function registerSchemaCommand(program2) {
781
+ program2.command("schema").description("\u4EE5 JSON \u8F93\u51FA\u5B8C\u6574\u547D\u4EE4\u6811\uFF08\u542B\u53C2\u6570\u3001\u6743\u9650\u8981\u6C42\u3001\u793A\u4F8B\uFF09\u548C\u9000\u51FA\u7801\u542B\u4E49\uFF0C\u4F9B agent/\u811A\u672C\u4E00\u6B21\u6027\u8BFB\u53D6").action(() => {
782
+ console.log(
783
+ JSON.stringify(
784
+ {
785
+ exitCodes: exitCodeTable(),
786
+ commands: program2.commands.map(serialize)
787
+ },
788
+ null,
789
+ 2
790
+ )
791
+ );
792
+ });
488
793
  }
489
794
 
490
795
  // src/index.ts
491
796
  var pkg = JSON.parse((0, import_fs2.readFileSync)((0, import_path2.join)(__dirname, "../package.json"), "utf8"));
492
797
  var program = new import_commander.Command();
493
- program.name("pdlab").description("1836 \u5F00\u6E90\u5171\u5EFA\u5E73\u53F0\u547D\u4EE4\u884C\u5DE5\u5177\u2014\u2014\u662F\u73B0\u6709 REST API \u7684\u8584\u5C01\u88C5").version(pkg.version);
798
+ program.name("pdlab").description("1836 \u5F00\u6E90\u5171\u5EFA\u5E73\u53F0\u547D\u4EE4\u884C\u5DE5\u5177\u2014\u2014\u662F\u73B0\u6709 REST API \u7684\u8584\u5C01\u88C5\u3002\u7ED9 agent/\u811A\u672C\u7528\uFF1Apdlab schema \u4E00\u6B21\u6027\u5217\u51FA\u5168\u90E8\u547D\u4EE4\u3001\u53C2\u6570\u4E0E\u9000\u51FA\u7801\u542B\u4E49\u3002").version(pkg.version);
494
799
  registerLoginCommand(program);
495
800
  registerRegisterCommand(program);
496
801
  registerLogoutCommand(program);
@@ -498,11 +803,7 @@ registerWhoamiCommand(program);
498
803
  registerConfigCommand(program);
499
804
  registerProjectCommand(program);
500
805
  registerPostCommand(program);
806
+ registerSchemaCommand(program);
501
807
  program.parseAsync(process.argv).catch((error) => {
502
- if (error instanceof ApiRequestError) {
503
- console.error(`\u9519\u8BEF\uFF1A${error.message}`);
504
- } else {
505
- console.error(error);
506
- }
507
- process.exit(1);
808
+ handleCommandError(error);
508
809
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdlab-cli",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "description": "1836 开源共建平台(problem-driven-lab)的命令行工具:项目、内容的增删改查,浏览器授权登录",
6
6
  "keywords": [