c456-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 +2 -0
  2. package/dist/index.js +244 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -129,6 +129,8 @@ bun run build
129
129
  node dist/index.js --help
130
130
  ```
131
131
 
132
+ 构建、发布与 **AI Agent 技能**(`npx skills add … --skill c456-cli`)见 [DEVELOPMENT.md](./DEVELOPMENT.md)。
133
+
132
134
  ## 许可证
133
135
 
134
136
  MIT
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.js
4
- import { Command as Command6 } from "commander";
4
+ import { Command as Command7 } from "commander";
5
5
 
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "c456-cli",
9
- version: "0.1.0",
9
+ version: "0.1.2",
10
10
  description: "C456 CLI - \u5185\u5BB9\u5F55\u5165\u4E0E\u6574\u7406\u5DE5\u5177",
11
11
  type: "module",
12
12
  bin: {
@@ -147,6 +147,36 @@ var ApiClient = class {
147
147
  });
148
148
  return this.handleResponse(res);
149
149
  }
150
+ /**
151
+ * POST multipart/form-data(用于上传文件)
152
+ * @param {string} path
153
+ * @param {Record<string, any>} fields
154
+ * @param {{ fieldName: string, filePath: string, filename?: string, contentType?: string } | null} file
155
+ */
156
+ async postMultipart(path, fields = {}, file = null) {
157
+ const url = `${this.baseUrl}/api/v1${path}`;
158
+ const form = new FormData();
159
+ Object.entries(fields || {}).forEach(([k, v]) => {
160
+ if (v === void 0 || v === null) return;
161
+ form.append(k, String(v));
162
+ });
163
+ if (file && file.filePath) {
164
+ const fs = await import("node:fs");
165
+ const pathMod = await import("node:path");
166
+ const bytes = fs.readFileSync(file.filePath);
167
+ const blob = new Blob([bytes], { type: file.contentType || "application/octet-stream" });
168
+ const name = file.filename || pathMod.basename(file.filePath);
169
+ form.append(file.fieldName, blob, name);
170
+ }
171
+ const headers = { ...this.headers() };
172
+ delete headers["Content-Type"];
173
+ const res = await fetch(url, {
174
+ method: "POST",
175
+ headers,
176
+ body: form
177
+ });
178
+ return this.handleResponse(res);
179
+ }
150
180
  /**
151
181
  * PATCH 请求
152
182
  */
@@ -159,6 +189,36 @@ var ApiClient = class {
159
189
  });
160
190
  return this.handleResponse(res);
161
191
  }
192
+ /**
193
+ * PATCH multipart/form-data(用于上传文件)
194
+ * @param {string} path
195
+ * @param {Record<string, any>} fields
196
+ * @param {{ fieldName: string, filePath: string, filename?: string, contentType?: string } | null} file
197
+ */
198
+ async patchMultipart(path, fields = {}, file = null) {
199
+ const url = `${this.baseUrl}/api/v1${path}`;
200
+ const form = new FormData();
201
+ Object.entries(fields || {}).forEach(([k, v]) => {
202
+ if (v === void 0 || v === null) return;
203
+ form.append(k, String(v));
204
+ });
205
+ if (file && file.filePath) {
206
+ const fs = await import("node:fs");
207
+ const pathMod = await import("node:path");
208
+ const bytes = fs.readFileSync(file.filePath);
209
+ const blob = new Blob([bytes], { type: file.contentType || "application/octet-stream" });
210
+ const name = file.filename || pathMod.basename(file.filePath);
211
+ form.append(file.fieldName, blob, name);
212
+ }
213
+ const headers = { ...this.headers() };
214
+ delete headers["Content-Type"];
215
+ const res = await fetch(url, {
216
+ method: "PATCH",
217
+ headers,
218
+ body: form
219
+ });
220
+ return this.handleResponse(res);
221
+ }
162
222
  /**
163
223
  * DELETE 请求
164
224
  */
@@ -212,7 +272,7 @@ function resolveApi(cmd) {
212
272
 
213
273
  // src/commands/intake.js
214
274
  var intake = new Command().name("intake").description("\u6536\u5F55\u7BA1\u7406 - \u521B\u5EFA\u3001\u66F4\u65B0\u3001\u5220\u9664\u5DE5\u5177/\u6E20\u9053/\u4FE1\u53F7");
215
- intake.command("new").description("\u521B\u5EFA\u65B0\u6536\u5F55").option("-u, --url <url>", "\u76EE\u6807 URL\uFF08tool/channel \u65F6\u53EF\u9009\uFF0C\u7528\u4E8E\u81EA\u52A8\u89E3\u6790\u8D44\u6599\uFF09").option("-k, --kind <type>", "\u7C7B\u578B\uFF1Asignal/tool/channel\uFF08\u9ED8\u8BA4 signal\uFF09", "signal").option("-t, --title <title>", "\u6807\u9898\uFF08tool/channel \u5FC5\u586B\uFF09").option("-b, --body <text>", "\u6B63\u6587/\u63CF\u8FF0").option("--profile-data-json <json>", "\u8D44\u6599\u6BB5 JSON\uFF08tool/channel\uFF09").action(async (opts, cmd) => {
275
+ intake.command("new").description("\u521B\u5EFA\u65B0\u6536\u5F55").option("-u, --url <url>", "\u76EE\u6807 URL\uFF08tool/channel \u65F6\u53EF\u9009\uFF0C\u7528\u4E8E\u81EA\u52A8\u89E3\u6790\u8D44\u6599\uFF09").option("-k, --kind <type>", "\u7C7B\u578B\uFF1Asignal/tool/channel\uFF08\u9ED8\u8BA4 signal\uFF09", "signal").option("-t, --title <title>", "\u6807\u9898\uFF08tool/channel \u5FC5\u586B\uFF09").option("-b, --body <text>", "\u6B63\u6587/\u63CF\u8FF0\uFF08type: markdown_kramdown\uFF1B\u8BED\u6CD5\u89C1 references/content-syntax-kramdown.md\uFF09").option("--profile-data-json <json>", "\u8D44\u6599\u6BB5 JSON\uFF08tool/channel\uFF09").action(async (opts, cmd) => {
216
276
  const { apiKey, baseUrl, client } = resolveApi(cmd);
217
277
  if (!apiKey) {
218
278
  console.error("\u9519\u8BEF\uFF1A\u672A\u914D\u7F6E API Key");
@@ -271,7 +331,7 @@ intake.command("show").description("\u67E5\u770B\u6536\u5F55\u8BE6\u60C5").argum
271
331
  process.exit(1);
272
332
  }
273
333
  });
274
- intake.command("update").description("\u66F4\u65B0\u6536\u5F55").argument("<id>", "\u6536\u5F55 ID").option("-t, --title <title>", "\u65B0\u6807\u9898").option("-b, --body <text>", "\u65B0\u6B63\u6587").option("--favorited", "\u6807\u8BB0\u4E3A\u6536\u85CF").option("--unfavorited", "\u53D6\u6D88\u6536\u85CF").action(async (id, opts, cmd) => {
334
+ intake.command("update").description("\u66F4\u65B0\u6536\u5F55").argument("<id>", "\u6536\u5F55 ID").option("-t, --title <title>", "\u65B0\u6807\u9898").option("-b, --body <text>", "\u65B0\u6B63\u6587\uFF08type: markdown_kramdown\uFF1B\u8BED\u6CD5\u89C1 references/content-syntax-kramdown.md\uFF09").option("--favorited", "\u6807\u8BB0\u4E3A\u6536\u85CF").option("--unfavorited", "\u53D6\u6D88\u6536\u85CF").action(async (id, opts, cmd) => {
275
335
  const { apiKey, client } = resolveApi(cmd);
276
336
  if (!apiKey) {
277
337
  console.error("\u9519\u8BEF\uFF1A\u672A\u914D\u7F6E API Key");
@@ -318,7 +378,7 @@ intake.command("delete").description("\u5220\u9664\u6536\u5F55").argument("<id>"
318
378
  process.exit(1);
319
379
  }
320
380
  });
321
- intake.command("list").description("\u5217\u51FA\u6536\u5F55\uFF08\u5206\u9875\uFF09").option("-k, --kind <type>", "\u7C7B\u578B\u8FC7\u6EE4\uFF1Asignal/tool/channel").option("-q, --query <text>", "\u641C\u7D22\u5173\u952E\u8BCD").option("-p, --page <num>", "\u9875\u7801", "1").option("-n, --per-page <num>", "\u6BCF\u9875\u6570\u91CF", "20").action(async (opts, cmd) => {
381
+ intake.command("list").description("\u5217\u51FA\u6536\u5F55\uFF08\u5206\u9875\uFF09").option("-k, --kind <type>", "\u7C7B\u578B\u8FC7\u6EE4\uFF1Asignal/tool/channel").option("-q, --query <text>", "\u641C\u7D22\u5173\u952E\u8BCD").option("-p, --page <num>", "\u9875\u7801\uFF081-10000\uFF09", "1").option("-n, --per-page <num>", "\u6BCF\u9875\u6570\u91CF\uFF081-100\uFF0C\u9ED8\u8BA4 20\uFF09", "20").action(async (opts, cmd) => {
322
382
  const { apiKey, client } = resolveApi(cmd);
323
383
  if (!apiKey) {
324
384
  console.error("\u9519\u8BEF\uFF1A\u672A\u914D\u7F6E API Key");
@@ -351,17 +411,23 @@ var intake_default = intake;
351
411
  // src/commands/fetch.js
352
412
  import { Command as Command2 } from "commander";
353
413
  var fetchProfile = new Command2().name("fetch").description("\u8D44\u6599\u6293\u53D6 - \u4ECE URL \u81EA\u52A8\u89E3\u6790\u5E73\u53F0\u8D44\u6599");
354
- fetchProfile.command("profile").description("\u6293\u53D6\u6307\u5B9A URL \u7684\u8D44\u6599\u6BB5\u6570\u636E").requiredOption("-u, --url <url>", "\u76EE\u6807 URL").option("-p, --profile-id <type>", "\u8D44\u6599\u7C7B\u578B\uFF1Alink_product/package_registry/github_origin/social_account").action(async (opts, cmd) => {
414
+ fetchProfile.command("profile").description("\u6293\u53D6\u6307\u5B9A URL \u7684\u8D44\u6599\u6BB5\u6570\u636E").requiredOption("-u, --url <url>", "\u76EE\u6807 URL").requiredOption(
415
+ "-p, --profile-id <type>",
416
+ [
417
+ "\u8D44\u6599\u7C7B\u578B\uFF08\u5FC5\u586B\uFF09\uFF1A",
418
+ "- link_product\uFF1A\u666E\u901A\u4EA7\u54C1/\u5B98\u7F51\u94FE\u63A5\uFF08\u89E3\u6790\u540D\u79F0\u3001\u56FE\u6807\u3001\u7B80\u4ECB\u7B49\uFF09",
419
+ "- package_registry\uFF1A\u8F6F\u4EF6\u5305\u5730\u5740\uFF08npm/RubyGems \u7B49\uFF09",
420
+ "- github_origin\uFF1A\u5F00\u6E90\u4ED3\u5E93\u5730\u5740\uFF08GitHub/GitLab/Gitee\uFF09",
421
+ "- social_account\uFF1A\u793E\u4EA4\u8D26\u53F7\u4E3B\u9875/\u9891\u9053\uFF08YouTube/\u6296\u97F3/\u5C0F\u7EA2\u4E66\u7B49\uFF09"
422
+ ].join("\n")
423
+ ).action(async (opts, cmd) => {
355
424
  const { apiKey, client } = resolveApi(cmd);
356
425
  if (!apiKey) {
357
426
  console.error("\u9519\u8BEF\uFF1A\u672A\u914D\u7F6E API Key");
358
427
  process.exit(1);
359
428
  }
360
429
  try {
361
- const body = { url: opts.url };
362
- if (opts.profileId) {
363
- body.profile_id = opts.profileId;
364
- }
430
+ const body = { url: opts.url, profile_id: opts.profileId };
365
431
  const result = await client.post("/fetches", body);
366
432
  const { data, suggested_title } = result.data;
367
433
  console.log("\u2705 \u8D44\u6599\u6293\u53D6\u6210\u529F");
@@ -473,7 +539,7 @@ var search_default = searchCmd;
473
539
  // src/commands/playbook.js
474
540
  import { Command as Command4 } from "commander";
475
541
  var playbookCmd = new Command4().name("playbook").description("\u6253\u6CD5\u7BA1\u7406 - \u521B\u5EFA\u3001\u66F4\u65B0\u3001\u5220\u9664\u6253\u6CD5");
476
- playbookCmd.command("new").description("\u521B\u5EFA\u65B0\u6253\u6CD5").requiredOption("-t, --title <title>", "\u6253\u6CD5\u6807\u9898").option("-b, --body <text>", "\u6253\u6CD5\u6B63\u6587\uFF08Markdown\uFF09").option("--ref-intake <id>", "\u5F15\u7528\u6536\u5F55 ID\uFF08\u53EF\u591A\u6B21\u6307\u5B9A\uFF09").option("--ref-playbook <id>", "\u5F15\u7528\u6253\u6CD5 ID\uFF08\u53EF\u591A\u6B21\u6307\u5B9A\uFF09").action(async (opts, cmd) => {
542
+ playbookCmd.command("new").description("\u521B\u5EFA\u65B0\u6253\u6CD5").requiredOption("-t, --title <title>", "\u6253\u6CD5\u6807\u9898").option("-b, --body <text>", "\u6253\u6CD5\u6B63\u6587\uFF08type: markdown_kramdown\uFF1B\u8BED\u6CD5\u89C1 references/content-syntax-kramdown.md\uFF09").option("--ref-intake <id>", "\u5F15\u7528\u6536\u5F55 ID\uFF08\u53EF\u591A\u6B21\u6307\u5B9A\uFF09").option("--ref-playbook <id>", "\u5F15\u7528\u6253\u6CD5 ID\uFF08\u53EF\u591A\u6B21\u6307\u5B9A\uFF09").action(async (opts, cmd) => {
477
543
  const { apiKey, client } = resolveApi(cmd);
478
544
  if (!apiKey) {
479
545
  console.error("\u9519\u8BEF\uFF1A\u672A\u914D\u7F6E API Key");
@@ -539,7 +605,7 @@ ${data.body || "(\u65E0)"}`);
539
605
  process.exit(1);
540
606
  }
541
607
  });
542
- playbookCmd.command("update").description("\u66F4\u65B0\u6253\u6CD5").argument("<id>", "\u6253\u6CD5 ID").option("-t, --title <title>", "\u65B0\u6807\u9898").option("-b, --body <text>", "\u65B0\u6B63\u6587").action(async (id, opts, cmd) => {
608
+ playbookCmd.command("update").description("\u66F4\u65B0\u6253\u6CD5").argument("<id>", "\u6253\u6CD5 ID").option("-t, --title <title>", "\u65B0\u6807\u9898").option("-b, --body <text>", "\u65B0\u6B63\u6587\uFF08type: markdown_kramdown\uFF1B\u8BED\u6CD5\u89C1 references/content-syntax-kramdown.md\uFF09").action(async (id, opts, cmd) => {
543
609
  const { apiKey, client } = resolveApi(cmd);
544
610
  if (!apiKey) {
545
611
  console.error("\u9519\u8BEF\uFF1A\u672A\u914D\u7F6E API Key");
@@ -584,7 +650,7 @@ playbookCmd.command("delete").description("\u5220\u9664\u6253\u6CD5").argument("
584
650
  process.exit(1);
585
651
  }
586
652
  });
587
- playbookCmd.command("list").description("\u5217\u51FA\u6253\u6CD5\uFF08\u5206\u9875\uFF09").option("-q, --query <text>", "\u641C\u7D22\u5173\u952E\u8BCD").option("-p, --page <num>", "\u9875\u7801", "1").option("-n, --per-page <num>", "\u6BCF\u9875\u6570\u91CF", "20").action(async (opts, cmd) => {
653
+ playbookCmd.command("list").description("\u5217\u51FA\u6253\u6CD5\uFF08\u5206\u9875\uFF09").option("-q, --query <text>", "\u641C\u7D22\u5173\u952E\u8BCD").option("-p, --page <num>", "\u9875\u7801\uFF081-10000\uFF09", "1").option("-n, --per-page <num>", "\u6BCF\u9875\u6570\u91CF\uFF081-100\uFF0C\u9ED8\u8BA4 20\uFF09", "20").action(async (opts, cmd) => {
588
654
  const { apiKey, client } = resolveApi(cmd);
589
655
  if (!apiKey) {
590
656
  console.error("\u9519\u8BEF\uFF1A\u672A\u914D\u7F6E API Key");
@@ -614,9 +680,170 @@ playbookCmd.command("list").description("\u5217\u51FA\u6253\u6CD5\uFF08\u5206\u9
614
680
  });
615
681
  var playbook_default = playbookCmd;
616
682
 
617
- // src/commands/config.js
683
+ // src/commands/walkthrough.js
618
684
  import { Command as Command5 } from "commander";
619
- var configCmd = new Command5().name("config").description("\u914D\u7F6E\u7BA1\u7406 - \u8BBE\u7F6E API Key \u548C\u7CFB\u7EDF\u5730\u5740");
685
+ var walkthroughCmd = new Command5().name("walkthrough").description("\u8BB2\u89E3\uFF08Walkthrough\uFF09\u7BA1\u7406 - \u521B\u5EFA\u3001\u66F4\u65B0\u3001\u5220\u9664\u8BB2\u89E3");
686
+ function requireApiKey(apiKey) {
687
+ if (!apiKey) {
688
+ console.error("\u9519\u8BEF\uFF1A\u672A\u914D\u7F6E API Key");
689
+ process.exit(1);
690
+ }
691
+ }
692
+ function ensureSourceKind(kind) {
693
+ const k = String(kind || "").trim() || "upload";
694
+ if (!["upload", "external_url"].includes(k)) {
695
+ console.error("\u9519\u8BEF\uFF1A--source-kind \u4EC5\u652F\u6301 upload \u6216 external_url");
696
+ process.exit(1);
697
+ }
698
+ return k;
699
+ }
700
+ function buildWalkthroughFields(opts) {
701
+ const w = {};
702
+ if (opts.title !== void 0) w.title = opts.title;
703
+ if (opts.summary !== void 0) w.summary = opts.summary;
704
+ if (opts.body !== void 0) w.body = opts.body;
705
+ if (opts.sourceKind !== void 0) w.source_kind = opts.sourceKind;
706
+ if (opts.externalUrl !== void 0) w.external_url = opts.externalUrl;
707
+ if (opts.posterAt !== void 0) w.poster_preview_at_seconds = opts.posterAt;
708
+ if (opts.publicationStatus !== void 0) w.publication_status = opts.publicationStatus;
709
+ return w;
710
+ }
711
+ walkthroughCmd.command("new").description("\u521B\u5EFA\u65B0\u8BB2\u89E3").requiredOption("-t, --title <title>", "\u6807\u9898").option("-s, --summary <text>", "\u6458\u8981").option("-b, --body <text>", "\u6B63\u6587\uFF08type: markdown_kramdown\uFF1B\u8BED\u6CD5\u89C1 references/content-syntax-kramdown.md\uFF09").option("--source-kind <kind>", "\u6765\u6E90\uFF1Aupload/external_url\uFF08\u9ED8\u8BA4 upload\uFF09", "upload").option("--external-url <url>", "asciinema.org \u94FE\u63A5\uFF08source-kind=external_url \u65F6\u5FC5\u586B\uFF09").option("--cast-file <path>", ".cast \u6587\u4EF6\u8DEF\u5F84\uFF08source-kind=upload \u65F6\u5FC5\u586B\uFF09").option("--poster-at <seconds>", "\u5C01\u9762\u9884\u89C8\u79D2\u6570\uFF08>=0 \u7684\u6574\u6570\uFF09").action(async (opts, cmd) => {
712
+ const { apiKey, client } = resolveApi(cmd);
713
+ requireApiKey(apiKey);
714
+ const sourceKind = ensureSourceKind(opts.sourceKind);
715
+ const posterAt = opts.posterAt !== void 0 ? Number.parseInt(String(opts.posterAt), 10) : void 0;
716
+ const w = {
717
+ title: opts.title,
718
+ summary: opts.summary || "",
719
+ body: opts.body || "",
720
+ source_kind: sourceKind,
721
+ external_url: opts.externalUrl || "",
722
+ poster_preview_at_seconds: Number.isFinite(posterAt) ? posterAt : void 0
723
+ };
724
+ if (sourceKind === "external_url") {
725
+ if (!w.external_url) {
726
+ console.error("\u9519\u8BEF\uFF1Asource-kind=external_url \u65F6\u5FC5\u987B\u63D0\u4F9B --external-url");
727
+ process.exit(1);
728
+ }
729
+ const result2 = await client.post("/walkthroughs", { walkthrough: w });
730
+ console.log("\u2705 \u8BB2\u89E3\u521B\u5EFA\u6210\u529F");
731
+ console.log(` ID: ${result2.data.id}`);
732
+ console.log(` \u6807\u9898\uFF1A${result2.data.title}`);
733
+ return;
734
+ }
735
+ if (!opts.castFile) {
736
+ console.error("\u9519\u8BEF\uFF1Asource-kind=upload \u65F6\u5FC5\u987B\u63D0\u4F9B --cast-file");
737
+ process.exit(1);
738
+ }
739
+ const fields = {
740
+ "walkthrough[title]": w.title,
741
+ "walkthrough[summary]": w.summary,
742
+ "walkthrough[body]": w.body,
743
+ "walkthrough[source_kind]": "upload"
744
+ };
745
+ if (w.poster_preview_at_seconds !== void 0) {
746
+ fields["walkthrough[poster_preview_at_seconds]"] = w.poster_preview_at_seconds;
747
+ }
748
+ const result = await client.postMultipart(
749
+ "/walkthroughs",
750
+ fields,
751
+ { fieldName: "walkthrough[cast_file]", filePath: opts.castFile, filename: "upload.cast" }
752
+ );
753
+ console.log("\u2705 \u8BB2\u89E3\u521B\u5EFA\u6210\u529F");
754
+ console.log(` ID: ${result.data.id}`);
755
+ console.log(` \u6807\u9898\uFF1A${result.data.title}`);
756
+ });
757
+ walkthroughCmd.command("list").description("\u5217\u51FA\u8BB2\u89E3\uFF08\u5206\u9875\uFF09").option("-q, --query <text>", "\u641C\u7D22\u5173\u952E\u8BCD").option("-p, --page <num>", "\u9875\u7801\uFF081-10000\uFF09", "1").option("-n, --per-page <num>", "\u6BCF\u9875\u6570\u91CF\uFF081-100\uFF0C\u9ED8\u8BA4 20\uFF09", "20").action(async (opts, cmd) => {
758
+ const { apiKey, client } = resolveApi(cmd);
759
+ requireApiKey(apiKey);
760
+ const result = await client.get("/walkthroughs", {
761
+ q: opts.query,
762
+ page: opts.page,
763
+ per_page: opts.perPage
764
+ });
765
+ const { data, meta } = result;
766
+ const perPage = metaPerPage(meta);
767
+ const totalPages = Math.max(1, Math.ceil(meta.total / perPage));
768
+ console.log(`\u5171 ${meta.total} \u6761\u8BB2\u89E3\uFF08\u7B2C ${meta.page}/${totalPages} \u9875\uFF09
769
+ `);
770
+ data.forEach((w) => {
771
+ console.log(`\u{1F3AC} [${w.id}] ${w.title}`);
772
+ if (w.durationLabel) console.log(` \u65F6\u957F\uFF1A${w.durationLabel}`);
773
+ if (w.summary) console.log(` ${w.summary}`);
774
+ });
775
+ });
776
+ walkthroughCmd.command("show").description("\u67E5\u770B\u8BB2\u89E3\u8BE6\u60C5").argument("<id>", "\u8BB2\u89E3 ID").action(async (id, opts, cmd) => {
777
+ const { apiKey, client } = resolveApi(cmd);
778
+ requireApiKey(apiKey);
779
+ const result = await client.get(`/walkthroughs/${id}`);
780
+ const w = result.data;
781
+ console.log(`ID: ${w.id}`);
782
+ console.log(`\u6807\u9898\uFF1A${w.title}`);
783
+ console.log(`\u72B6\u6001\uFF1A${w.publicationStatus}`);
784
+ if (w.summary) console.log(`\u6458\u8981\uFF1A${w.summary}`);
785
+ console.log(`\u6B63\u6587\uFF1A
786
+ ${w.body || "(\u65E0)"}`);
787
+ console.log(`\u6765\u6E90\uFF1A${w.sourceKind}`);
788
+ if (w.src) console.log(`\u5A92\u4F53\uFF1A${w.src}`);
789
+ });
790
+ walkthroughCmd.command("update").description("\u66F4\u65B0\u8BB2\u89E3").argument("<id>", "\u8BB2\u89E3 ID").option("-t, --title <title>", "\u65B0\u6807\u9898").option("-s, --summary <text>", "\u65B0\u6458\u8981").option("-b, --body <text>", "\u65B0\u6B63\u6587\uFF08type: markdown_kramdown\uFF1B\u8BED\u6CD5\u89C1 references/content-syntax-kramdown.md\uFF09").option("--publication-status <status>", "\u53D1\u5E03\u72B6\u6001\uFF1Apending_review/private").option("--source-kind <kind>", "\u6765\u6E90\uFF1Aupload/external_url").option("--external-url <url>", "asciinema.org \u94FE\u63A5\uFF08source-kind=external_url\uFF09").option("--cast-file <path>", ".cast \u6587\u4EF6\u8DEF\u5F84\uFF08\u4E0A\u4F20\u66FF\u6362\uFF09").option("--poster-at <seconds>", "\u5C01\u9762\u9884\u89C8\u79D2\u6570\uFF08>=0 \u7684\u6574\u6570\uFF09").action(async (id, opts, cmd) => {
791
+ const { apiKey, client } = resolveApi(cmd);
792
+ requireApiKey(apiKey);
793
+ const posterAt = opts.posterAt !== void 0 ? Number.parseInt(String(opts.posterAt), 10) : void 0;
794
+ const w = buildWalkthroughFields({
795
+ title: opts.title,
796
+ summary: opts.summary,
797
+ body: opts.body,
798
+ sourceKind: opts.sourceKind ? ensureSourceKind(opts.sourceKind) : void 0,
799
+ externalUrl: opts.externalUrl,
800
+ posterAt: Number.isFinite(posterAt) ? posterAt : void 0,
801
+ publicationStatus: opts.publicationStatus
802
+ });
803
+ const hasFile = Boolean(opts.castFile);
804
+ if (!hasFile) {
805
+ const result2 = await client.patch(`/walkthroughs/${id}`, { walkthrough: w });
806
+ console.log("\u2705 \u8BB2\u89E3\u66F4\u65B0\u6210\u529F");
807
+ console.log(` \u6807\u9898\uFF1A${result2.data.title}`);
808
+ return;
809
+ }
810
+ const fields = {};
811
+ Object.entries(w).forEach(([k, v]) => {
812
+ fields[`walkthrough[${k}]`] = v;
813
+ });
814
+ const result = await client.patchMultipart(
815
+ `/walkthroughs/${id}`,
816
+ fields,
817
+ { fieldName: "walkthrough[cast_file]", filePath: opts.castFile, filename: "upload.cast" }
818
+ );
819
+ console.log("\u2705 \u8BB2\u89E3\u66F4\u65B0\u6210\u529F");
820
+ console.log(` \u6807\u9898\uFF1A${result.data.title}`);
821
+ });
822
+ walkthroughCmd.command("delete").description("\u5220\u9664\u8BB2\u89E3").argument("<id>", "\u8BB2\u89E3 ID").option("-f, --force", "\u5F3A\u5236\u5220\u9664\uFF08\u65E0\u9700\u786E\u8BA4\uFF09").action(async (id, opts, cmd) => {
823
+ const { apiKey, client } = resolveApi(cmd);
824
+ requireApiKey(apiKey);
825
+ if (!opts.force) {
826
+ const readline = await import("node:readline");
827
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
828
+ const answer = await new Promise((resolve) => {
829
+ rl.question("\u786E\u8BA4\u5220\u9664\uFF1F(y/N): ", (ans) => {
830
+ rl.close();
831
+ resolve(ans.toLowerCase());
832
+ });
833
+ });
834
+ if (answer !== "y" && answer !== "yes") {
835
+ console.log("\u5DF2\u53D6\u6D88");
836
+ return;
837
+ }
838
+ }
839
+ await client.delete(`/walkthroughs/${id}`);
840
+ console.log("\u2705 \u8BB2\u89E3\u5DF2\u5220\u9664");
841
+ });
842
+ var walkthrough_default = walkthroughCmd;
843
+
844
+ // src/commands/config.js
845
+ import { Command as Command6 } from "commander";
846
+ var configCmd = new Command6().name("config").description("\u914D\u7F6E\u7BA1\u7406 - \u8BBE\u7F6E API Key \u548C\u7CFB\u7EDF\u5730\u5740");
620
847
  configCmd.command("set-key").description("\u8BBE\u7F6E API Key").argument("<token>", "API Key \u4EE4\u724C").action((token, cmd) => {
621
848
  const config = loadConfig();
622
849
  config.apiKey = token;
@@ -712,7 +939,7 @@ ${body}
712
939
  }
713
940
 
714
941
  // src/index.js
715
- var program = new Command6();
942
+ var program = new Command7();
716
943
  program.name("c456").description("C456 CLI - \u5FEB\u901F\u5185\u5BB9\u5F55\u5165\u4E0E\u6574\u7406\u5DE5\u5177").version(package_default.version);
717
944
  program.addHelpText("before", () => getHelpBanner());
718
945
  program.option(
@@ -723,6 +950,7 @@ program.addCommand(intake_default);
723
950
  program.addCommand(fetch_default);
724
951
  program.addCommand(search_default);
725
952
  program.addCommand(playbook_default);
953
+ program.addCommand(walkthrough_default);
726
954
  program.addCommand(config_default);
727
955
  program.on("--help", () => {
728
956
  console.log("\n\u793A\u4F8B:");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c456-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "C456 CLI - 内容录入与整理工具",
5
5
  "type": "module",
6
6
  "bin": {