pdlab-cli 0.2.1 → 0.3.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 +16 -3
  2. package/dist/index.js +225 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -20,7 +20,7 @@ npm install -g pdlab-cli
20
20
  pdlab login
21
21
  ```
22
22
 
23
- 默认连官方部署 `https://1836.online`,装完直接就能用。
23
+ 默认连官方部署 `https://www.1836.online`,装完直接就能用。
24
24
  自建实例才需要指定地址,指定过一次之后会记在配置文件里:
25
25
 
26
26
  ```bash
@@ -50,8 +50,10 @@ pdlab schema # 一次性 JSON 输出全部命令、参数、权限要
50
50
  | `pdlab project join \| leave <id>` | 报名共建 / 撤回申请或退出共建 |
51
51
  | `pdlab project member review \| set-role \| remove <projectId> <userId>` | 审核报名、设置协作者角色、移除参与者(仅项目发起人本人) |
52
52
  | `pdlab project revenue <id>` | 查看项目的积分分成明细(只读) |
53
- | `pdlab post list \| get \| create \| update \| delete` | 内容的增删改查 |
53
+ | `pdlab post list \| get \| create \| update \| delete` | 内容的增删改查(`--project-id` 选填,不填即独立发布,不挂任何项目) |
54
54
  | `pdlab post review <id> --action approve\|reject` | 审核一条待审内容 |
55
+ | `pdlab taxonomy list \| create \| update \| delete` | 分类标签的增删改查(列表公开只读,写操作需 `taxonomy:manage`) |
56
+ | `pdlab taxonomy dimension list \| create \| update \| delete <key>` | 分类维度的增删改查(内置维度不可删) |
55
57
 
56
58
  任何命令加 `--help` 看具体参数——示例已经直接打在 `--help` 末尾,不用等一次报错才能看到。
57
59
 
@@ -88,6 +90,17 @@ pdlab post list --project-id <项目 id>
88
90
  # 审核待审内容(需要 content:review 权限)
89
91
  pdlab post review post_123 --action approve
90
92
  pdlab post review post_123 --action reject --comment "缺少数据来源,请补充引用"
93
+
94
+ # 独立发布内容(不挂任何项目),任何已生效账号都能发;非免审角色发出去要等发起人审
95
+ pdlab post create --title "钠电产业观察" --body "……"
96
+
97
+ # 分类维度与标签管理(需要 taxonomy:manage 权限,即「1836发起人」角色)
98
+ pdlab taxonomy dimension list # 公开只读
99
+ pdlab taxonomy dimension create --key region --label "地区" --sort-order 3
100
+ pdlab taxonomy create --dimension domain --label "钠电固态化" --weight 0.2
101
+ pdlab taxonomy update tax_123 --weight 0.3
102
+ pdlab taxonomy delete tax_123
103
+ pdlab taxonomy dimension delete region # 内置维度(domain/attribute/commercial)不可删
91
104
  ```
92
105
 
93
106
  ### 分页
@@ -127,7 +140,7 @@ pdlab post review post_123 --action reject --comment "缺少数据来源,请
127
140
  ```
128
141
 
129
142
  API 地址的解析顺序:`--api-url` 参数 > `PDLAB_API_URL` 环境变量 > 配置文件 > 默认地址
130
- (`https://1836.online`)。所有命令都支持 `--api-url`,
143
+ (`https://www.1836.online`)。所有命令都支持 `--api-url`,
131
144
  包括 `project` / `post` 这些资源命令。`pdlab config get` 会同时打出配置文件里写了什么、
132
145
  以及这次实际会用哪个地址。
133
146
 
package/dist/index.js CHANGED
@@ -48,7 +48,7 @@ var CliError = class extends Error {
48
48
  // src/config.ts
49
49
  var CONFIG_DIR = process.env.PDLAB_CONFIG_DIR ?? (0, import_path.join)((0, import_os.homedir)(), ".pdlab");
50
50
  var CONFIG_FILE = (0, import_path.join)(CONFIG_DIR, "config.json");
51
- var DEFAULT_API_URL = "https://1836.online";
51
+ var DEFAULT_API_URL = "https://www.1836.online";
52
52
  function readConfig() {
53
53
  if (!(0, import_fs.existsSync)(CONFIG_FILE)) return {};
54
54
  try {
@@ -297,27 +297,42 @@ function isSafeHttpUrl(raw) {
297
297
  return ALLOWED_PROTOCOLS.has(url.protocol);
298
298
  }
299
299
  var safeHttpUrl = import_zod.z.string().trim().max(2048, "\u94FE\u63A5\u8FC7\u957F").refine(isSafeHttpUrl, "\u53EA\u652F\u6301 http/https \u5F00\u5934\u7684\u94FE\u63A5");
300
+ var INTERNAL_IMAGE_PATH = /^\/api\/images\/[a-z0-9]{1,64}$/;
301
+ function isInternalImagePath(raw) {
302
+ return INTERNAL_IMAGE_PATH.test(raw.trim());
303
+ }
304
+ function isSafeImageSrc(raw) {
305
+ return isInternalImagePath(raw) || isSafeHttpUrl(raw);
306
+ }
307
+ var safeImageUrl = import_zod.z.string().trim().max(2048, "\u94FE\u63A5\u8FC7\u957F").refine(isSafeImageSrc, "\u53EA\u652F\u6301 http/https \u94FE\u63A5\u6216\u5E73\u53F0\u5185\u4E0A\u4F20\u7684\u56FE\u7247");
300
308
 
301
309
  // ../shared-schemas/src/projects.ts
302
310
  var import_zod2 = require("zod");
311
+ var taxonomySelectionSchema = import_zod2.z.object({
312
+ taxonomyId: import_zod2.z.string().min(1).describe("\u5206\u7C7B\u6807\u7B7E id"),
313
+ weight: import_zod2.z.number().min(0).max(1).describe("\u8BE5\u6807\u7B7E\u5728\u672C\u9879\u76EE\u91CC\u7684\u6743\u91CD 0~1")
314
+ });
303
315
  var PROJECT_STATUS_VALUES = ["planning", "active", "review", "completed"];
304
316
  var projectCreateSchema = import_zod2.z.object({
305
317
  title: import_zod2.z.string().trim().min(1, "\u9879\u76EE\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A").max(200).describe("\u9879\u76EE\u6807\u9898"),
306
318
  description: import_zod2.z.string().max(2e4).optional().describe("\u9879\u76EE\u63CF\u8FF0"),
307
- coverImage: safeHttpUrl.optional().describe("\u5C01\u9762\u56FE http/https \u94FE\u63A5"),
308
- commercialType: import_zod2.z.string().max(100).optional().describe("\u5546\u4E1A\u5C5E\u6027"),
319
+ coverImage: safeImageUrl.optional().describe("\u5C01\u9762\u56FE\u94FE\u63A5\u6216\u4E0A\u4F20\u56FE\u7247\u5730\u5740"),
320
+ commercialType: import_zod2.z.string().max(100).optional().describe("\u5546\u4E1A\u5408\u4F5C\u72B6\u6001\uFF0C\u5982\u300C\u4E0E\u67D0IDC\u5382\u5546\u63A5\u6D3D\u4E2D\u300D"),
309
321
  status: import_zod2.z.enum(PROJECT_STATUS_VALUES).optional().describe("\u9879\u76EE\u72B6\u6001\uFF1Aplanning/active/review/completed"),
310
322
  progress: import_zod2.z.number().int().min(0).max(100).optional().describe("\u9879\u76EE\u8FDB\u5EA6\u767E\u5206\u6BD4 0-100"),
311
- taxonomyIds: import_zod2.z.array(import_zod2.z.string().min(1)).max(50).optional().default([]).describe("\u5206\u7C7B\u6807\u7B7E id \u5217\u8868")
323
+ taxonomies: import_zod2.z.array(taxonomySelectionSchema).max(50).optional().describe("\u5206\u7C7B\u6807\u7B7E\u53CA\u5176\u5728\u672C\u9879\u76EE\u91CC\u7684\u6743\u91CD\uFF0C\u6BCF\u4E2A\u7EF4\u5EA6\u6700\u591A\u4E00\u4E2A\u6807\u7B7E"),
324
+ // 兼容 pdlab CLI 的旧参数:只给 id、不给权重时,服务端按标签的全局建议权重(Taxonomy.weight)填充
325
+ taxonomyIds: import_zod2.z.array(import_zod2.z.string().min(1)).max(50).optional().default([]).describe("\u5206\u7C7B\u6807\u7B7E id \u5217\u8868\uFF08\u65E7\u53C2\u6570\uFF0C\u6743\u91CD\u53D6\u6807\u7B7E\u9ED8\u8BA4\u503C\uFF09")
312
326
  });
313
327
  var projectUpdateSchema = import_zod2.z.object({
314
328
  title: import_zod2.z.string().trim().min(1, "\u9879\u76EE\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A").max(200).optional().describe("\u9879\u76EE\u6807\u9898"),
315
329
  description: import_zod2.z.string().max(2e4).nullable().optional().describe("\u9879\u76EE\u63CF\u8FF0"),
316
- coverImage: safeHttpUrl.nullable().optional().describe("\u5C01\u9762\u56FE http/https \u94FE\u63A5"),
317
- commercialType: import_zod2.z.string().max(100).nullable().optional().describe("\u5546\u4E1A\u5C5E\u6027"),
330
+ coverImage: safeImageUrl.nullable().optional().describe("\u5C01\u9762\u56FE\u94FE\u63A5\u6216\u4E0A\u4F20\u56FE\u7247\u5730\u5740"),
331
+ commercialType: import_zod2.z.string().max(100).nullable().optional().describe("\u5546\u4E1A\u5408\u4F5C\u72B6\u6001"),
318
332
  progress: import_zod2.z.number().int().min(0).max(100).optional().describe("\u9879\u76EE\u8FDB\u5EA6\u767E\u5206\u6BD4 0-100"),
319
333
  status: import_zod2.z.enum(PROJECT_STATUS_VALUES).optional().describe("\u9879\u76EE\u72B6\u6001\uFF1Aplanning/active/review/completed"),
320
- taxonomyIds: import_zod2.z.array(import_zod2.z.string().min(1)).max(50).optional().describe("\u5206\u7C7B\u6807\u7B7E id \u5217\u8868\uFF08\u5168\u91CF\u66FF\u6362\uFF09")
334
+ taxonomies: import_zod2.z.array(taxonomySelectionSchema).max(50).optional().describe("\u5206\u7C7B\u6807\u7B7E\u53CA\u5176\u6743\u91CD\uFF08\u5168\u91CF\u66FF\u6362\uFF09"),
335
+ taxonomyIds: import_zod2.z.array(import_zod2.z.string().min(1)).max(50).optional().describe("\u5206\u7C7B\u6807\u7B7E id \u5217\u8868\uFF08\u65E7\u53C2\u6570\uFF0C\u5168\u91CF\u66FF\u6362\uFF0C\u6743\u91CD\u53D6\u6807\u7B7E\u9ED8\u8BA4\u503C\uFF09")
321
336
  }).refine((v) => Object.keys(v).length > 0, "\u6CA1\u6709\u9700\u8981\u66F4\u65B0\u7684\u5B57\u6BB5");
322
337
 
323
338
  // ../shared-schemas/src/posts.ts
@@ -325,22 +340,52 @@ var import_zod3 = require("zod");
325
340
  var postCreateSchema = import_zod3.z.object({
326
341
  title: import_zod3.z.string().min(1).describe("\u5185\u5BB9\u6807\u9898"),
327
342
  body: import_zod3.z.string().min(1).describe("\u5185\u5BB9\u6B63\u6587"),
328
- projectId: import_zod3.z.string().describe("\u6240\u5C5E\u9879\u76EE id"),
329
- mediaLinks: import_zod3.z.array(safeHttpUrl).max(20, "\u5A92\u4F53\u94FE\u63A5\u6700\u591A 20 \u6761").optional().default([]).describe("\u5A92\u4F53\u94FE\u63A5\u5217\u8868")
343
+ // V3.1:可以不挂项目独立发布。挂项目时仍然要求发布者是该项目的发起人或已通过审核的参与者。
344
+ projectId: import_zod3.z.string().min(1).nullish().describe("\u6240\u5C5E\u9879\u76EE id\uFF0C\u4E0D\u586B\u8868\u793A\u72EC\u7ACB\u53D1\u5E03"),
345
+ mediaLinks: import_zod3.z.array(safeImageUrl).max(20, "\u5A92\u4F53\u4E0E\u56FE\u7247\u6700\u591A 20 \u6761").optional().default([]).describe("\u4E0A\u4F20\u7684\u56FE\u7247\u5730\u5740\u3001\u89C6\u9891\u94FE\u63A5\u4E0E\u5916\u90E8\u94FE\u63A5")
330
346
  });
331
347
  var postUpdateSchema = import_zod3.z.object({
332
348
  title: import_zod3.z.string().trim().min(1, "\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A").max(200).describe("\u5185\u5BB9\u6807\u9898"),
333
349
  body: import_zod3.z.string().trim().min(1, "\u6B63\u6587\u4E0D\u80FD\u4E3A\u7A7A").max(5e4).describe("\u5185\u5BB9\u6B63\u6587"),
334
- mediaLinks: import_zod3.z.array(safeHttpUrl).max(20).optional().default([]).describe("\u5A92\u4F53\u94FE\u63A5\u5217\u8868")
350
+ mediaLinks: import_zod3.z.array(safeImageUrl).max(20).optional().default([]).describe("\u4E0A\u4F20\u7684\u56FE\u7247\u5730\u5740\u3001\u89C6\u9891\u94FE\u63A5\u4E0E\u5916\u90E8\u94FE\u63A5")
335
351
  });
336
352
  var postReviewSchema = import_zod3.z.object({
337
353
  action: import_zod3.z.enum(["approve", "reject"]).describe("\u5BA1\u6838\u7ED3\u679C\uFF1Aapprove/reject"),
338
354
  reviewComment: import_zod3.z.string().trim().max(500, "\u5BA1\u6838\u610F\u89C1\u6700\u591A 500 \u5B57").optional().describe("\u5BA1\u6838\u610F\u89C1\uFF08\u9A73\u56DE\u65F6\u5FC5\u586B\uFF09")
339
355
  });
340
356
 
357
+ // ../shared-schemas/src/taxonomies.ts
358
+ var import_zod4 = require("zod");
359
+ var taxonomyDimensionCreateSchema = import_zod4.z.object({
360
+ // key 会进 URL 查询串和导出文件,限制成 slug 形态
361
+ key: import_zod4.z.string().trim().min(1).max(40).regex(/^[a-z][a-z0-9_]*$/, "\u7EF4\u5EA6\u6807\u8BC6\u53EA\u80FD\u7528\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u4E0B\u5212\u7EBF\uFF0C\u4E14\u4EE5\u5B57\u6BCD\u5F00\u5934").describe("\u7EF4\u5EA6\u6807\u8BC6\uFF0C\u5982 domain\uFF08\u521B\u5EFA\u540E\u4E0D\u53EF\u6539\uFF09"),
362
+ label: import_zod4.z.string().trim().min(1, "\u7EF4\u5EA6\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A").max(40).describe("\u7EF4\u5EA6\u4E2D\u6587\u540D\uFF0C\u5982\u300C\u9886\u57DF\u300D"),
363
+ description: import_zod4.z.string().max(500).optional().describe("\u7EF4\u5EA6\u8BF4\u660E"),
364
+ sortOrder: import_zod4.z.number().int().min(0).max(999).optional().default(0).describe("\u5C55\u793A\u6392\u5E8F")
365
+ });
366
+ var taxonomyDimensionUpdateSchema = import_zod4.z.object({
367
+ label: import_zod4.z.string().trim().min(1).max(40).optional().describe("\u7EF4\u5EA6\u4E2D\u6587\u540D"),
368
+ description: import_zod4.z.string().max(500).nullable().optional().describe("\u7EF4\u5EA6\u8BF4\u660E"),
369
+ sortOrder: import_zod4.z.number().int().min(0).max(999).optional().describe("\u5C55\u793A\u6392\u5E8F")
370
+ });
371
+ var taxonomyCreateSchema = import_zod4.z.object({
372
+ dimension: import_zod4.z.string().min(1).describe("\u6240\u5C5E\u7EF4\u5EA6\u7684 key\uFF0C\u5982 domain"),
373
+ label: import_zod4.z.string().trim().min(1, "\u6807\u7B7E\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A").max(100).describe("\u6807\u7B7E\u503C\uFF0C\u5982\u300C\u94A0\u7535\u4E0EAIDC\u5168\u7403\u57FA\u5EFA\u300D"),
374
+ weight: import_zod4.z.number().min(0).max(1).optional().default(0).describe("\u5EFA\u8BAE\u6743\u91CD 0~1\uFF1A\u53D1\u8D77\u9879\u76EE\u65F6\u9884\u586B\u7ED9\u53D1\u8D77\u4EBA\uFF0C\u53EF\u518D\u8C03"),
375
+ sortOrder: import_zod4.z.number().int().min(0).max(999).optional().default(0).describe("\u5C55\u793A\u6392\u5E8F"),
376
+ description: import_zod4.z.string().max(500).optional().describe("\u6807\u7B7E\u8BF4\u660E")
377
+ });
378
+ var taxonomyUpdateSchema = import_zod4.z.object({
379
+ dimension: import_zod4.z.string().min(1).optional().describe("\u6240\u5C5E\u7EF4\u5EA6\u7684 key"),
380
+ label: import_zod4.z.string().min(1).optional().describe("\u6807\u7B7E\u503C"),
381
+ weight: import_zod4.z.number().min(0).max(1).optional().describe("\u5EFA\u8BAE\u6743\u91CD 0~1"),
382
+ sortOrder: import_zod4.z.number().int().optional().describe("\u5C55\u793A\u6392\u5E8F"),
383
+ description: import_zod4.z.string().optional().describe("\u6807\u7B7E\u8BF4\u660E")
384
+ });
385
+
341
386
  // src/errors.ts
342
387
  var import_commander = require("commander");
343
- var import_zod4 = require("zod");
388
+ var import_zod5 = require("zod");
344
389
  var EXIT_CODES = {
345
390
  ok: 0,
346
391
  unhandled: 1,
@@ -373,7 +418,7 @@ function exampleHint(examples) {
373
418
  }
374
419
  function handleCommandError(error, examples = []) {
375
420
  let structured;
376
- if (error instanceof import_zod4.ZodError) {
421
+ if (error instanceof import_zod5.ZodError) {
377
422
  structured = {
378
423
  error: "\u53C2\u6570\u6821\u9A8C\u5931\u8D25",
379
424
  code: "validation",
@@ -636,9 +681,13 @@ function registerProjectCommand(program2) {
636
681
 
637
682
  // src/commands/post.ts
638
683
  var CREATE_EXAMPLES2 = [
684
+ {
685
+ command: 'pdlab post create --title "\u94A0\u7535\u4EA7\u4E1A\u89C2\u5BDF" --body "..."',
686
+ note: "\u53EA\u6709 title / body \u5FC5\u586B\uFF1A\u4E0D\u5E26 --project-id \u5C31\u662F\u72EC\u7ACB\u53D1\u5E03\uFF0C\u4E0D\u6302\u4EFB\u4F55\u5171\u5EFA\u9879\u76EE"
687
+ },
639
688
  {
640
689
  command: 'pdlab post create --title "\u7B2C\u4E00\u5468\u8FDB\u5C55" --body "..." --project-id proj_123 --media-links https://example.com/a.png',
641
- 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"
690
+ note: "\u6302\u5230\u9879\u76EE\u4E0B\u65F6\uFF0C\u9700\u8981\u662F\u8BE5\u9879\u76EE\u53D1\u8D77\u4EBA\u6216\u5DF2\u901A\u8FC7\u5BA1\u6838\u7684\u53C2\u4E0E\u8005"
642
691
  }
643
692
  ];
644
693
  var UPDATE_EXAMPLES2 = [
@@ -646,6 +695,7 @@ var UPDATE_EXAMPLES2 = [
646
695
  ];
647
696
  var LIST_EXAMPLES2 = [
648
697
  { command: "pdlab post list --project-id proj_123", note: "\u53EA\u770B\u67D0\u4E2A\u9879\u76EE\u4E0B\u7684\u5185\u5BB9" },
698
+ { command: "pdlab post list --project-id none", note: "\u53EA\u770B\u72EC\u7ACB\u53D1\u5E03\uFF08\u4E0D\u6302\u4EFB\u4F55\u9879\u76EE\uFF09\u7684\u5185\u5BB9" },
649
699
  { 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" },
650
700
  { 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" }
651
701
  ];
@@ -670,7 +720,7 @@ function toMediaLinks(value) {
670
720
  function registerPostCommand(program2) {
671
721
  const post = program2.command("post").description("\u5185\u5BB9\uFF08\u5E16\u5B50\uFF09\u7684\u589E\u5220\u6539\u67E5\u4E0E\u5BA1\u6838");
672
722
  attachMeta(
673
- 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) => {
723
+ 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\uFF1B\u4F20 none \u53EA\u770B\u72EC\u7ACB\u53D1\u5E03\uFF08\u4E0D\u6302\u9879\u76EE\uFF09\u7684\u5185\u5BB9").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) => {
674
724
  const data = await apiRequest("/api/posts", {
675
725
  query: { projectId: opts.projectId, status: opts.status, limit: opts.limit, cursor: opts.cursor },
676
726
  token: optionalToken(),
@@ -690,7 +740,7 @@ function registerPostCommand(program2) {
690
740
  { examples: GET_EXAMPLES2 }
691
741
  );
692
742
  attachMeta(
693
- 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) => {
743
+ 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\u9009\u586B\uFF0C\u4E0D\u586B\u5373\u72EC\u7ACB\u53D1\u5E03\uFF09").option("--media-links <urls>", "\u9017\u53F7\u5206\u9694\u7684\u56FE\u7247/\u89C6\u9891/\u5916\u94FE\u5730\u5740\uFF08\u9009\u586B\uFF0C\u6700\u591A 20 \u6761\uFF09").action(async (opts) => {
694
744
  const token = requireToken();
695
745
  try {
696
746
  const body = postCreateSchema.parse({
@@ -705,10 +755,10 @@ function registerPostCommand(program2) {
705
755
  handleCommandError(error, CREATE_EXAMPLES2);
706
756
  }
707
757
  }),
708
- { permission: "content:create\uFF0C\u4E14\u662F\u9879\u76EE\u53D1\u8D77\u4EBA\u6216\u5DF2\u62A5\u540D\u5171\u5EFA\u8BE5\u9879\u76EE", examples: CREATE_EXAMPLES2 }
758
+ { permission: "content:create\uFF1B\u5E26 --project-id \u65F6\u8FD8\u9700\u662F\u8BE5\u9879\u76EE\u53D1\u8D77\u4EBA\u6216\u5DF2\u901A\u8FC7\u5BA1\u6838\u7684\u53C2\u4E0E\u8005", examples: CREATE_EXAMPLES2 }
709
759
  );
710
760
  attachMeta(
711
- 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) => {
761
+ 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\u56FE\u7247/\u89C6\u9891/\u5916\u94FE\u5730\u5740\uFF08\u9009\u586B\uFF0C\u6700\u591A 20 \u6761\uFF09").action(async (id, opts) => {
712
762
  const token = requireToken();
713
763
  try {
714
764
  const body = postUpdateSchema.parse({
@@ -749,6 +799,163 @@ function registerPostCommand(program2) {
749
799
  );
750
800
  }
751
801
 
802
+ // src/commands/taxonomy.ts
803
+ var PERMISSION = "taxonomy:manage\uFF08\u4EC5\u300C1836\u53D1\u8D77\u4EBA\u300D\u89D2\u8272\uFF09";
804
+ var DIM_LIST_EXAMPLES = [
805
+ { command: "pdlab taxonomy dimension list", note: "\u516C\u5F00\u53EA\u8BFB\uFF0C\u4E0D\u767B\u5F55\u4E5F\u80FD\u7528" }
806
+ ];
807
+ var DIM_CREATE_EXAMPLES = [
808
+ {
809
+ command: 'pdlab taxonomy dimension create --key region --label "\u5730\u533A" --sort-order 3',
810
+ note: "key \u53EA\u80FD\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF\u4E14\u4EE5\u5B57\u6BCD\u5F00\u5934\uFF0C\u5EFA\u597D\u4E4B\u540E\u4E0D\u80FD\u6539"
811
+ }
812
+ ];
813
+ var DIM_UPDATE_EXAMPLES = [
814
+ { command: 'pdlab taxonomy dimension update domain --label "\u9886\u57DF\uFF08\u66F4\u65B0\uFF09"', note: "key \u4E0D\u53EF\u6539\uFF0C\u53EA\u80FD\u6539\u5C55\u793A\u5C5E\u6027" }
815
+ ];
816
+ var DIM_DELETE_EXAMPLES = [
817
+ {
818
+ command: "pdlab taxonomy dimension delete region",
819
+ note: "\u5185\u7F6E\u7EF4\u5EA6\uFF08domain/attribute/commercial\uFF09\u4E0D\u53EF\u5220\uFF1B\u7EF4\u5EA6\u4E0B\u8FD8\u6709\u6807\u7B7E\u65F6\u4F1A 409"
820
+ }
821
+ ];
822
+ var LIST_EXAMPLES3 = [{ command: "pdlab taxonomy list", note: "\u516C\u5F00\u53EA\u8BFB\uFF0C\u4E0D\u767B\u5F55\u4E5F\u80FD\u7528" }];
823
+ var CREATE_EXAMPLES3 = [
824
+ {
825
+ command: 'pdlab taxonomy create --dimension domain --label "\u94A0\u7535\u56FA\u6001\u5316" --weight 0.2',
826
+ note: "dimension \u5FC5\u987B\u662F\u5DF2\u5B58\u5728\u7684\u7EF4\u5EA6 key\uFF1Bweight \u662F\u53D1\u8D77\u9879\u76EE\u65F6\u9884\u586B\u7ED9\u53D1\u8D77\u4EBA\u7684\u5EFA\u8BAE\u503C\uFF0C\u4E0D\u662F\u6700\u7EC8\u53C2\u4E0E\u8BA1\u7B97\u7684\u90A3\u4EFD"
827
+ }
828
+ ];
829
+ var UPDATE_EXAMPLES3 = [
830
+ { command: "pdlab taxonomy update tax_123 --weight 0.3", note: "\u53EA\u4F20\u8981\u6539\u7684\u5B57\u6BB5" }
831
+ ];
832
+ var DELETE_EXAMPLES3 = [{ command: "pdlab taxonomy delete tax_123" }];
833
+ function toDimensionBody(opts) {
834
+ const body = {};
835
+ if (opts.key !== void 0) body.key = opts.key;
836
+ if (opts.label !== void 0) body.label = opts.label;
837
+ if (opts.description !== void 0) body.description = opts.description;
838
+ if (opts.sortOrder !== void 0) body.sortOrder = Number(opts.sortOrder);
839
+ return body;
840
+ }
841
+ function toTaxonomyBody(opts) {
842
+ const body = {};
843
+ if (opts.dimension !== void 0) body.dimension = opts.dimension;
844
+ if (opts.label !== void 0) body.label = opts.label;
845
+ if (opts.weight !== void 0) body.weight = Number(opts.weight);
846
+ if (opts.sortOrder !== void 0) body.sortOrder = Number(opts.sortOrder);
847
+ if (opts.description !== void 0) body.description = opts.description;
848
+ return body;
849
+ }
850
+ function registerTaxonomyCommand(program2) {
851
+ const taxonomy = program2.command("taxonomy").description("\u5206\u7C7B\u7EF4\u5EA6\u4E0E\u5206\u7C7B\u6807\u7B7E\u7684\u589E\u5220\u6539\u67E5\uFF08PRD 4.2.4\uFF09");
852
+ const dimension = taxonomy.command("dimension").description("\u5206\u7C7B\u7EF4\u5EA6\uFF08\u9886\u57DF/\u5C5E\u6027/\u5546\u4E1A\u2026\u2026\uFF09\u7684\u589E\u5220\u6539\u67E5");
853
+ attachMeta(
854
+ withApiUrl(dimension.command("list").description("\u5217\u51FA\u5168\u90E8\u5206\u7C7B\u7EF4\u5EA6\uFF08\u516C\u5F00\u53EA\u8BFB\uFF0C\u4E0D\u767B\u5F55\u4E5F\u80FD\u7528\uFF09")).action(
855
+ async (opts) => {
856
+ const data = await apiRequest("/api/taxonomy-dimensions", { token: optionalToken(), apiUrl: opts.apiUrl });
857
+ console.log(JSON.stringify(data, null, 2));
858
+ }
859
+ ),
860
+ { examples: DIM_LIST_EXAMPLES }
861
+ );
862
+ attachMeta(
863
+ withApiUrl(dimension.command("create").description("\u65B0\u5EFA\u5206\u7C7B\u7EF4\u5EA6")).option("--key <key>", "\u7EF4\u5EA6\u6807\u8BC6\uFF08\u5FC5\u586B\uFF0C\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF\uFF0C\u4EE5\u5B57\u6BCD\u5F00\u5934\uFF0C\u521B\u5EFA\u540E\u4E0D\u53EF\u6539\uFF09").option("--label <label>", "\u7EF4\u5EA6\u4E2D\u6587\u540D\uFF08\u5FC5\u586B\uFF09").option("--description <text>", "\u7EF4\u5EA6\u8BF4\u660E\uFF08\u9009\u586B\uFF09").option("--sort-order <n>", "\u5C55\u793A\u6392\u5E8F\uFF0C\u6574\u6570 0-999\uFF08\u9009\u586B\uFF09").action(async (opts) => {
864
+ const token = requireToken();
865
+ try {
866
+ const body = taxonomyDimensionCreateSchema.parse(toDimensionBody(opts));
867
+ const data = await apiRequest("/api/taxonomy-dimensions", {
868
+ method: "POST",
869
+ body,
870
+ token,
871
+ apiUrl: opts.apiUrl
872
+ });
873
+ console.log(JSON.stringify(data, null, 2));
874
+ } catch (error) {
875
+ handleCommandError(error, DIM_CREATE_EXAMPLES);
876
+ }
877
+ }),
878
+ { permission: PERMISSION, examples: DIM_CREATE_EXAMPLES }
879
+ );
880
+ attachMeta(
881
+ withApiUrl(dimension.command("update <key>").description("\u66F4\u65B0\u5206\u7C7B\u7EF4\u5EA6\u7684\u5C55\u793A\u5C5E\u6027\uFF08key \u672C\u8EAB\u4E0D\u53EF\u6539\uFF09")).option("--label <label>", "\u7EF4\u5EA6\u4E2D\u6587\u540D").option("--description <text>", "\u7EF4\u5EA6\u8BF4\u660E").option("--sort-order <n>", "\u5C55\u793A\u6392\u5E8F\uFF0C\u6574\u6570 0-999").action(async (key, opts) => {
882
+ const token = requireToken();
883
+ try {
884
+ const body = taxonomyDimensionUpdateSchema.parse(toDimensionBody(opts));
885
+ const data = await apiRequest(`/api/taxonomy-dimensions/${key}`, {
886
+ method: "PATCH",
887
+ body,
888
+ token,
889
+ apiUrl: opts.apiUrl
890
+ });
891
+ console.log(JSON.stringify(data, null, 2));
892
+ } catch (error) {
893
+ handleCommandError(error, DIM_UPDATE_EXAMPLES);
894
+ }
895
+ }),
896
+ { permission: PERMISSION, examples: DIM_UPDATE_EXAMPLES }
897
+ );
898
+ attachMeta(
899
+ withApiUrl(
900
+ dimension.command("delete <key>").description("\u5220\u9664\u5206\u7C7B\u7EF4\u5EA6\uFF08\u5185\u7F6E\u7EF4\u5EA6\u4E0D\u53EF\u5220\uFF1B\u7EF4\u5EA6\u4E0B\u8FD8\u6709\u6807\u7B7E\u65F6\u4F1A 409\uFF09")
901
+ ).action(async (key, opts) => {
902
+ const token = requireToken();
903
+ const data = await apiRequest(`/api/taxonomy-dimensions/${key}`, {
904
+ method: "DELETE",
905
+ token,
906
+ apiUrl: opts.apiUrl
907
+ });
908
+ console.log(JSON.stringify(data, null, 2));
909
+ }),
910
+ { permission: PERMISSION, examples: DIM_DELETE_EXAMPLES }
911
+ );
912
+ attachMeta(
913
+ withApiUrl(taxonomy.command("list").description("\u5217\u51FA\u5168\u90E8\u5206\u7C7B\u6807\u7B7E\uFF08\u516C\u5F00\u53EA\u8BFB\uFF0C\u4E0D\u767B\u5F55\u4E5F\u80FD\u7528\uFF09")).action(
914
+ async (opts) => {
915
+ const data = await apiRequest("/api/taxonomies", { token: optionalToken(), apiUrl: opts.apiUrl });
916
+ console.log(JSON.stringify(data, null, 2));
917
+ }
918
+ ),
919
+ { examples: LIST_EXAMPLES3 }
920
+ );
921
+ attachMeta(
922
+ withApiUrl(taxonomy.command("create").description("\u65B0\u5EFA\u5206\u7C7B\u6807\u7B7E")).option("--dimension <key>", "\u6240\u5C5E\u7EF4\u5EA6\u7684 key\uFF08\u5FC5\u586B\uFF0C\u987B\u662F\u5DF2\u5B58\u5728\u7684\u7EF4\u5EA6\uFF09").option("--label <label>", "\u6807\u7B7E\u503C\uFF08\u5FC5\u586B\uFF09").option("--weight <n>", "\u5EFA\u8BAE\u6743\u91CD 0~1\uFF08\u9009\u586B\uFF0C\u9ED8\u8BA4 0\uFF09").option("--sort-order <n>", "\u5C55\u793A\u6392\u5E8F\uFF0C\u6574\u6570 0-999\uFF08\u9009\u586B\uFF09").option("--description <text>", "\u6807\u7B7E\u8BF4\u660E\uFF08\u9009\u586B\uFF09").action(async (opts) => {
923
+ const token = requireToken();
924
+ try {
925
+ const body = taxonomyCreateSchema.parse(toTaxonomyBody(opts));
926
+ const data = await apiRequest("/api/taxonomies", { method: "POST", body, token, apiUrl: opts.apiUrl });
927
+ console.log(JSON.stringify(data, null, 2));
928
+ } catch (error) {
929
+ handleCommandError(error, CREATE_EXAMPLES3);
930
+ }
931
+ }),
932
+ { permission: PERMISSION, examples: CREATE_EXAMPLES3 }
933
+ );
934
+ attachMeta(
935
+ withApiUrl(taxonomy.command("update <id>").description("\u66F4\u65B0\u5206\u7C7B\u6807\u7B7E")).option("--dimension <key>", "\u6240\u5C5E\u7EF4\u5EA6\u7684 key").option("--label <label>", "\u6807\u7B7E\u503C").option("--weight <n>", "\u5EFA\u8BAE\u6743\u91CD 0~1").option("--sort-order <n>", "\u5C55\u793A\u6392\u5E8F").option("--description <text>", "\u6807\u7B7E\u8BF4\u660E").action(async (id, opts) => {
936
+ const token = requireToken();
937
+ try {
938
+ const body = taxonomyUpdateSchema.parse(toTaxonomyBody(opts));
939
+ const data = await apiRequest(`/api/taxonomies/${id}`, { method: "PATCH", body, token, apiUrl: opts.apiUrl });
940
+ console.log(JSON.stringify(data, null, 2));
941
+ } catch (error) {
942
+ handleCommandError(error, UPDATE_EXAMPLES3);
943
+ }
944
+ }),
945
+ { permission: PERMISSION, examples: UPDATE_EXAMPLES3 }
946
+ );
947
+ attachMeta(
948
+ withApiUrl(taxonomy.command("delete <id>").description("\u5220\u9664\u5206\u7C7B\u6807\u7B7E")).action(
949
+ async (id, opts) => {
950
+ const token = requireToken();
951
+ const data = await apiRequest(`/api/taxonomies/${id}`, { method: "DELETE", token, apiUrl: opts.apiUrl });
952
+ console.log(JSON.stringify(data, null, 2));
953
+ }
954
+ ),
955
+ { permission: PERMISSION, examples: DELETE_EXAMPLES3 }
956
+ );
957
+ }
958
+
752
959
  // src/commands/schema.ts
753
960
  var CODE_DESCRIPTION = {
754
961
  ok: "\u6210\u529F",
@@ -809,6 +1016,7 @@ registerWhoamiCommand(program);
809
1016
  registerConfigCommand(program);
810
1017
  registerProjectCommand(program);
811
1018
  registerPostCommand(program);
1019
+ registerTaxonomyCommand(program);
812
1020
  registerSchemaCommand(program);
813
1021
  program.parseAsync(process.argv).catch((error) => {
814
1022
  if (error instanceof import_commander2.CommanderError && error.exitCode === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdlab-cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "1836 开源共建平台(problem-driven-lab)的命令行工具:项目、内容的增删改查,浏览器授权登录",
6
6
  "keywords": [