pdlab-cli 0.1.0 → 0.1.2

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 +15 -2
  2. package/dist/index.js +68 -36
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -17,7 +17,13 @@ npm install -g pdlab-cli
17
17
  ## 快速开始
18
18
 
19
19
  ```bash
20
- # 首次使用需要告诉它服务器地址,之后会记住
20
+ pdlab login
21
+ ```
22
+
23
+ 默认连官方部署 `https://1836.online`,装完直接就能用。
24
+ 自建实例才需要指定地址,指定过一次之后会记在配置文件里:
25
+
26
+ ```bash
21
27
  pdlab login --api-url https://your-1836-instance.example.com
22
28
  ```
23
29
 
@@ -71,7 +77,14 @@ pdlab post list --project-id <项目 id>
71
77
  { "apiUrl": "https://your-1836-instance.example.com", "token": "pdlab_…" }
72
78
  ```
73
79
 
74
- API 地址的解析顺序:`--api-url` 参数 > `PDLAB_API_URL` 环境变量 > 配置文件。
80
+ API 地址的解析顺序:`--api-url` 参数 > `PDLAB_API_URL` 环境变量 > 配置文件 > 默认地址
81
+ (`https://1836.online`)。所有命令都支持 `--api-url`,
82
+ 包括 `project` / `post` 这些资源命令。`pdlab config get` 会同时打出配置文件里写了什么、
83
+ 以及这次实际会用哪个地址。
84
+
85
+ 读命令(`project list` / `get`、`post list` / `get`)会在**已登录时带上凭证**,
86
+ 可见范围因此按你的身份走:有审核权限才能 `post list --status pending`,
87
+ 作者也才能 `post get` 到自己那篇还没过审的稿子。没登录则匿名读公开内容,照常可用。
75
88
  在 CI 里可以只给环境变量,不落配置文件。
76
89
 
77
90
  ## 凭证与安全
package/dist/index.js CHANGED
@@ -35,8 +35,9 @@ 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
- var CONFIG_DIR = (0, import_path.join)((0, import_os.homedir)(), ".pdlab");
38
+ var CONFIG_DIR = process.env.PDLAB_CONFIG_DIR ?? (0, import_path.join)((0, import_os.homedir)(), ".pdlab");
39
39
  var CONFIG_FILE = (0, import_path.join)(CONFIG_DIR, "config.json");
40
+ var DEFAULT_API_URL = "https://1836.online";
40
41
  function readConfig() {
41
42
  if (!(0, import_fs.existsSync)(CONFIG_FILE)) return {};
42
43
  try {
@@ -55,13 +56,12 @@ function updateConfig(patch) {
55
56
  return next;
56
57
  }
57
58
  function resolveApiUrl(flagValue) {
58
- const apiUrl = flagValue ?? process.env.PDLAB_API_URL ?? readConfig().apiUrl;
59
- if (!apiUrl) {
60
- console.error("\u672A\u914D\u7F6E API \u5730\u5740\u3002\u8BF7\u5148\u8FD0\u884C `pdlab login` / `pdlab register`\uFF0C\u6216\u8BBE\u7F6E PDLAB_API_URL \u73AF\u5883\u53D8\u91CF\u3002");
61
- process.exit(1);
62
- }
59
+ const apiUrl = flagValue ?? process.env.PDLAB_API_URL ?? readConfig().apiUrl ?? DEFAULT_API_URL;
63
60
  return apiUrl.replace(/\/+$/, "");
64
61
  }
62
+ function optionalToken() {
63
+ return readConfig().token;
64
+ }
65
65
  function requireToken() {
66
66
  const token = readConfig().token;
67
67
  if (!token) {
@@ -139,7 +139,7 @@ async function pollCliAuth(code, apiUrl) {
139
139
 
140
140
  // src/commands/login.ts
141
141
  function registerLoginCommand(program2) {
142
- program2.command("login").description("\u5728\u6D4F\u89C8\u5668\u91CC\u5B8C\u6210\u4E00\u6B21\u6027\u6388\u6743\uFF0C\u767B\u5F55 pdlab CLI\uFF08token \u957F\u671F\u6709\u6548\uFF0C\u76F4\u5230\u88AB\u64A4\u9500\uFF09").option("--api-url <url>", "API \u670D\u52A1\u5668\u5730\u5740\uFF0C\u672A\u914D\u7F6E\u8FC7\u65F6\u5FC5\u987B\u63D0\u4F9B\u4E00\u6B21").action(async (opts) => {
142
+ program2.command("login").description("\u5728\u6D4F\u89C8\u5668\u91CC\u5B8C\u6210\u4E00\u6B21\u6027\u6388\u6743\uFF0C\u767B\u5F55 pdlab CLI\uFF08token \u957F\u671F\u6709\u6548\uFF0C\u76F4\u5230\u88AB\u64A4\u9500\uFF09").option("--api-url <url>", "API \u670D\u52A1\u5668\u5730\u5740\uFF0C\u9ED8\u8BA4\u662F\u5B98\u65B9\u90E8\u7F72\uFF0C\u81EA\u5EFA\u5B9E\u4F8B\u624D\u9700\u8981\u586B").action(async (opts) => {
143
143
  const apiUrl = resolveApiUrl(opts.apiUrl);
144
144
  const start = await apiRequest("/api/cli/auth/start", {
145
145
  method: "POST",
@@ -171,7 +171,7 @@ function registerLoginCommand(program2) {
171
171
  // src/commands/register.ts
172
172
  var import_open2 = __toESM(require("open"));
173
173
  function registerRegisterCommand(program2) {
174
- program2.command("register").description("\u5728\u6D4F\u89C8\u5668\u91CC\u5B8C\u6210\u6CE8\u518C\u7533\u8BF7\uFF08\u6CE8\u518C\u540E\u9700\u7BA1\u7406\u5458\u5BA1\u6838\uFF0CCLI \u4E0D\u4F1A\u81EA\u52A8\u767B\u5F55\uFF09").option("--api-url <url>", "API \u670D\u52A1\u5668\u5730\u5740\uFF0C\u672A\u914D\u7F6E\u8FC7\u65F6\u5FC5\u987B\u63D0\u4F9B\u4E00\u6B21").action(async (opts) => {
174
+ program2.command("register").description("\u5728\u6D4F\u89C8\u5668\u91CC\u5B8C\u6210\u6CE8\u518C\u7533\u8BF7\uFF08\u6CE8\u518C\u540E\u9700\u7BA1\u7406\u5458\u5BA1\u6838\uFF0CCLI \u4E0D\u4F1A\u81EA\u52A8\u767B\u5F55\uFF09").option("--api-url <url>", "API \u670D\u52A1\u5668\u5730\u5740\uFF0C\u9ED8\u8BA4\u662F\u5B98\u65B9\u90E8\u7F72\uFF0C\u81EA\u5EFA\u5B9E\u4F8B\u624D\u9700\u8981\u586B").action(async (opts) => {
175
175
  const apiUrl = resolveApiUrl(opts.apiUrl);
176
176
  const start = await apiRequest("/api/cli/auth/start", {
177
177
  method: "POST",
@@ -224,8 +224,19 @@ function registerWhoamiCommand(program2) {
224
224
  function registerConfigCommand(program2) {
225
225
  const config = program2.command("config").description("\u67E5\u770B/\u4FEE\u6539\u672C\u5730\u914D\u7F6E\uFF08~/.pdlab/config.json\uFF09");
226
226
  config.command("get").description("\u6253\u5370\u5F53\u524D\u914D\u7F6E").action(() => {
227
- const { token, ...rest } = readConfig();
228
- console.log(JSON.stringify({ ...rest, token: token ? "***" : void 0 }, null, 2));
227
+ const { token, apiUrl } = readConfig();
228
+ console.log(
229
+ JSON.stringify(
230
+ {
231
+ apiUrl,
232
+ token: token ? "***" : void 0,
233
+ effectiveApiUrl: resolveApiUrl(),
234
+ defaultApiUrl: DEFAULT_API_URL
235
+ },
236
+ null,
237
+ 2
238
+ )
239
+ );
229
240
  });
230
241
  config.command("set <key> <value>").description("\u8BBE\u7F6E\u4E00\u9879\u914D\u7F6E\uFF0C\u76EE\u524D\u652F\u6301\uFF1Aapi-url").action((key, value) => {
231
242
  if (key !== "api-url") {
@@ -330,6 +341,11 @@ function handleCommandError(error, examples) {
330
341
  process.exit(1);
331
342
  }
332
343
 
344
+ // src/commands/options.ts
345
+ function withApiUrl(cmd) {
346
+ return cmd.option("--api-url <url>", "API \u670D\u52A1\u5668\u5730\u5740\uFF0C\u8986\u76D6 PDLAB_API_URL\u3001\u914D\u7F6E\u6587\u4EF6\u4E0E\u9ED8\u8BA4\u5730\u5740");
347
+ }
348
+
333
349
  // src/commands/project.ts
334
350
  var CREATE_EXAMPLES = [
335
351
  {
@@ -354,24 +370,30 @@ function toBody(opts) {
354
370
  return body;
355
371
  }
356
372
  function withProjectFlags(cmd) {
357
- return 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");
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");
358
374
  }
359
375
  function registerProjectCommand(program2) {
360
376
  const project = program2.command("project").description("\u9879\u76EE\u7684\u589E\u5220\u6539\u67E5");
361
- project.command("list").description("\u5217\u51FA\u9879\u76EE").option("--status <status>", "\u6309\u72B6\u6001\u7B5B\u9009\uFF1Aplanning | active | review | completed").action(async (opts) => {
362
- const data = await apiRequest("/api/projects", { query: { status: opts.status } });
363
- console.log(JSON.stringify(data, null, 2));
364
- });
365
- project.command("get <id>").description("\u67E5\u770B\u9879\u76EE\u8BE6\u60C5").action(async (id) => {
366
- const data = await apiRequest(`/api/projects/${id}`);
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
+ });
367
383
  console.log(JSON.stringify(data, null, 2));
368
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
+ }
390
+ );
369
391
  withProjectFlags(project.command("create").description("\u521B\u5EFA\u9879\u76EE\uFF08\u9700\u8981\u9879\u76EE\u521B\u5EFA\u6743\u9650\uFF09")).action(
370
392
  async (opts) => {
371
393
  const token = requireToken();
372
394
  try {
373
395
  const body = projectCreateSchema.parse(toBody(opts));
374
- const data = await apiRequest("/api/projects", { method: "POST", body, token });
396
+ const data = await apiRequest("/api/projects", { method: "POST", body, token, apiUrl: opts.apiUrl });
375
397
  console.log(JSON.stringify(data, null, 2));
376
398
  } catch (error) {
377
399
  handleCommandError(error, CREATE_EXAMPLES);
@@ -383,16 +405,18 @@ function registerProjectCommand(program2) {
383
405
  const token = requireToken();
384
406
  try {
385
407
  const body = projectUpdateSchema.parse(toBody(opts));
386
- const data = await apiRequest(`/api/projects/${id}`, { method: "PATCH", body, token });
408
+ const data = await apiRequest(`/api/projects/${id}`, { method: "PATCH", body, token, apiUrl: opts.apiUrl });
387
409
  console.log(JSON.stringify(data, null, 2));
388
410
  } catch (error) {
389
411
  handleCommandError(error, UPDATE_EXAMPLES);
390
412
  }
391
413
  }
392
414
  );
393
- 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").action(async (id) => {
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) => {
394
418
  const token = requireToken();
395
- const data = await apiRequest(`/api/projects/${id}`, { method: "DELETE", token });
419
+ const data = await apiRequest(`/api/projects/${id}`, { method: "DELETE", token, apiUrl: opts.apiUrl });
396
420
  console.log(JSON.stringify(data, null, 2));
397
421
  });
398
422
  }
@@ -411,15 +435,21 @@ function toMediaLinks(value) {
411
435
  }
412
436
  function registerPostCommand(program2) {
413
437
  const post = program2.command("post").description("\u5185\u5BB9\uFF08\u5E16\u5B50\uFF09\u7684\u589E\u5220\u6539\u67E5");
414
- 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) => {
415
- const data = await apiRequest("/api/posts", { query: { projectId: opts.projectId, status: opts.status } });
416
- console.log(JSON.stringify(data, null, 2));
417
- });
418
- post.command("get <id>").description("\u67E5\u770B\u5185\u5BB9\u8BE6\u60C5").action(async (id) => {
419
- const data = await apiRequest(`/api/posts/${id}`);
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
+ });
420
444
  console.log(JSON.stringify(data, null, 2));
421
445
  });
422
- 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) => {
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) => {
423
453
  const token = requireToken();
424
454
  try {
425
455
  const body = postCreateSchema.parse({
@@ -428,13 +458,13 @@ function registerPostCommand(program2) {
428
458
  projectId: opts.projectId,
429
459
  mediaLinks: toMediaLinks(opts.mediaLinks)
430
460
  });
431
- const data = await apiRequest("/api/posts", { method: "POST", body, token });
461
+ const data = await apiRequest("/api/posts", { method: "POST", body, token, apiUrl: opts.apiUrl });
432
462
  console.log(JSON.stringify(data, null, 2));
433
463
  } catch (error) {
434
464
  handleCommandError(error, CREATE_EXAMPLES2);
435
465
  }
436
466
  });
437
- 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) => {
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) => {
438
468
  const token = requireToken();
439
469
  try {
440
470
  const body = postUpdateSchema.parse({
@@ -442,17 +472,19 @@ function registerPostCommand(program2) {
442
472
  body: opts.body,
443
473
  mediaLinks: toMediaLinks(opts.mediaLinks)
444
474
  });
445
- const data = await apiRequest(`/api/posts/${id}`, { method: "PATCH", body, token });
475
+ const data = await apiRequest(`/api/posts/${id}`, { method: "PATCH", body, token, apiUrl: opts.apiUrl });
446
476
  console.log(JSON.stringify(data, null, 2));
447
477
  } catch (error) {
448
478
  handleCommandError(error, UPDATE_EXAMPLES2);
449
479
  }
450
480
  });
451
- 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(async (id) => {
452
- const token = requireToken();
453
- const data = await apiRequest(`/api/posts/${id}`, { method: "DELETE", token });
454
- console.log(JSON.stringify(data, null, 2));
455
- });
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) => {
483
+ 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
+ }
487
+ );
456
488
  }
457
489
 
458
490
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdlab-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "description": "1836 开源共建平台(problem-driven-lab)的命令行工具:项目、内容的增删改查,浏览器授权登录",
6
6
  "keywords": [