<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>CLI on Kmoon Blog</title>
    <link>https://blog.kmoon.fun/blog/cli/</link>
    <description>Recent content in CLI on Kmoon Blog</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    
      <managingEditor>hushan@kmoon.fun (Kmoon)</managingEditor>
    
    
      <webMaster>hushan@kmoon.fun (Kmoon)</webMaster>
    
    
    <copyright>Copyright © 2020-2026. Kmoon.</copyright>
    
    
    <lastBuildDate>Thu, 18 Jun 2026 11:28:59 +0800</lastBuildDate>
    
    
    <follow_challenge>
        <feedId>00000000000000000</feedId>
        <userId>00000000000000000</userId>
    </follow_challenge>
    
    <atom:link href="https://blog.kmoon.fun/blog/cli/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>luckin CLI：实现在终端里点杯瑞幸咖啡</title>
      <link>https://blog.kmoon.fun/luckin-cli%E5%AE%9E%E7%8E%B0%E5%9C%A8%E7%BB%88%E7%AB%AF%E9%87%8C%E7%82%B9%E6%9D%AF%E7%91%9E%E5%B9%B8%E5%92%96%E5%95%A1/</link>
      <pubDate>Thu, 18 Jun 2026 11:28:59 +0800</pubDate><author>hushan@kmoon.fun (Kmoon)</author>
      <guid>https://blog.kmoon.fun/luckin-cli%E5%AE%9E%E7%8E%B0%E5%9C%A8%E7%BB%88%E7%AB%AF%E9%87%8C%E7%82%B9%E6%9D%AF%E7%91%9E%E5%B9%B8%E5%92%96%E5%95%A1/</guid>
      <description>&lt;p&gt;上周末加班，今天调休，相当于提前放端午假了哈哈。&lt;/p&gt;&#xA;&lt;p&gt;瑞幸咖啡太能整活了，听说出了瑞幸咖啡 CLI 服务，可以在终端调用 CLI 服务，让下单更加便捷。刚好前几天女朋友跟我说瑞幸出了一个新品，“弗朗明戈-跳跳冰茶”，说里面有跳跳躺，喝起来很神奇。我们今天就来尝尝看，顺便看看这个 CLI 是整活还是真能用！&lt;/p&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T05-07-49-696Z.png&#34; width=200/&gt;&#xA;&lt;p&gt;首先，访问&lt;a href=&#34;https://open.lkcoffee.com/cli&#34;&gt;瑞幸咖啡AI开放平台&lt;/a&gt;，可以看到网站做的还是很简洁科技风的。&lt;/p&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T03-31-46-639Z.png&#34; width=500/&gt;&#xA;&lt;p&gt;我们按照官网教程，安装 CLI 工具：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -fsSL https://open.lkcoffee.com/install &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T03-36-16-259Z.png&#34; width=500/&gt;&#xA;&lt;p&gt;我们可以看到已经成功安装了 luckin 0.0.1 CLI 工具。接下来我们在终端输入 luckin 回车：&lt;/p&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T03-38-18-773Z.png&#34; width=500/&gt;&#xA;&lt;p&gt;需要先登录授权一下，这里可以看到是跳转到浏览器官网进行授权登录，然后创建一个 Token。&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://cdn.kmoon.fun/2026/2026-06-18T03-39-51-212Z.png&#34; width=300/&gt;&#xA;&lt;img src=&#34;https://cdn.kmoon.fun/2026/2026-06-18T03-40-18-960Z.png&#34; width=300/&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;然后我们返回终端，可以看到已经登录成功了，并且可以看到支持哪些斜杠命令：&lt;/p&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T03-41-02-898Z.png&#34; width=500/&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T03-47-39-742Z.png&#34; width=500/&gt;&#xA;&lt;p&gt;我们先查看一下附近门店，使用 /store 命令，这里突然离谱了起来，这个命令居然需要我自己输入经纬度？？？&lt;/p&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T03-49-24-902Z.png&#34; width=500/&gt;&#xA;&lt;p&gt;算了，我们还是在 Claude Code 里面使用它的 my-coffee Skill 吧。希望后续可以打通定位服务，自己定位用户所在位置。&lt;/p&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T03-54-20-618Z.png&#34; width=500/&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T04-38-06-550Z.png&#34; width=500/&gt;&#xA;&lt;p&gt;我们将官网创建的 Token 手动复制过来，并告诉它在附近帮我点一杯新品冰茶：&lt;/p&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T04-41-08-607Z.png&#34; width=500/&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T04-54-50-192Z.png&#34; width=500/&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T04-56-28-201Z.png&#34; width=500/&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T04-57-40-940Z.png&#34; width=500/&gt;&#xA;&lt;p&gt;这里需要我们扫码支付一下，支付完成后会帮我们查询订单状态和取餐码。这里我们可以发现目前只支持自取，暂时不支持外卖配送。不过我相信不久的将来，当美团 CLI、淘宝闪购 CLI 出现的时候，是完全可以支持外卖配送的。&lt;/p&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T04-59-52-774Z.png&#34; width=500/&gt;&#xA;&lt;p&gt;好啦，现在我们可以准备去门店取餐了。&lt;/p&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T05-03-13-746Z.png&#34; width=500/&gt;&#xA;&lt;p&gt;打开手机的小程序，我们可以看到订单制作完成，等待取餐中：&lt;/p&gt;&#xA;&lt;img src= &#34;https://cdn.kmoon.fun/2026/2026-06-18T05-06-18-362Z.png&#34; width=300/&gt;&#xA;&lt;p&gt;也是成功喝到新品了，一直滋滋滋，跟小时候吃的跳跳糖一样。&lt;/p&gt;&#xA;&lt;img src=&#34;https://cdn.kmoon.fun/2026/2026-06-18T05-24-51-962Z.png&#34; alt=&#34;&#34; width=300/&gt;&#xA;&lt;h2 id=&#34;写在最后&#34;&gt;写在最后&lt;/h2&gt;&#xA;&lt;p&gt;这次折腾这么久，最后也确实成功完成了在终端里下单一杯咖啡。瑞幸这次不是简单整活，而是提供了完整的一套 MCP + CLI + Skills，同时也提供了工具文档，这体现了瑞幸选择拥抱 AI 这条路上迈出了第一步。&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>zhlgd-cli：尝试做一个 CLI</title>
      <link>https://blog.kmoon.fun/zhlgd-cli%E5%B0%9D%E8%AF%95%E5%81%9A%E4%B8%80%E4%B8%AA-cli/</link>
      <pubDate>Sun, 07 Jun 2026 17:01:02 +0800</pubDate><author>hushan@kmoon.fun (Kmoon)</author>
      <guid>https://blog.kmoon.fun/zhlgd-cli%E5%B0%9D%E8%AF%95%E5%81%9A%E4%B8%80%E4%B8%AA-cli/</guid>
      <description>&lt;p&gt;一直觉得在命令行里敲命令然后回车，有一种莫名的爽感，特别是当黑框里返回自己想要的东西的时候。&lt;/p&gt;&#xA;&lt;p&gt;AI 这两年发展很快，CLI（Command Line Interface）也被重新推到了舞台中央。平时我们常用的 GUI，对人类当然很友好，但对 Agent 来说未必是最高效的工作方式。能直接发命令、拿结果、继续串联下一步，这种交互反而更适合 AI 干活。&lt;/p&gt;&#xA;&lt;p&gt;目前互联网上最火的 CLI 之一，应该就是字节跳动开源的 &lt;a href=&#34;https://github.com/larksuite/cli/blob/main/README.zh.md&#34;&gt;飞书 CLI（lark-cli）&lt;/a&gt;。它能让人类和 Agent 都在终端里操作飞书，比如收发消息、创建文档、多维表格、查询日历和会议等等。&lt;/p&gt;&#xA;&lt;p&gt;CLI 对 Agent 来说，本质上也是一种工具调用，而且很多时候还是更顺手的一种工具。所以，为了紧跟 AI 发展，我决定尝试自己写一个 CLI，看看这东西到底是怎么落地的。&lt;/p&gt;&#xA;&lt;h2 id=&#34;先想需求&#34;&gt;先想需求&lt;/h2&gt;&#xA;&lt;p&gt;不管干什么，总得结合实际需求，毕竟没实际用途，做着也没劲。仔细想了想，我日常能接触到的场景也就来自两方面：学校、公司。公司这边肯定没法下手，压根不需要我；字节这边每天都会有层出不穷的工具，各种花色任你选择，说实话真有点“军备竞赛”那味道了。&lt;/p&gt;&#xA;&lt;p&gt;所以我把思路转移到学校这边。学校这边能做的感觉挺多，毕竟学校的网站、各种平台都比较老旧，有些交互也确实不够顺手。突然想到智慧理工大，武理一个大家日常都会用到的大 OA 平台，而且最近还刚改版，那就干脆拿它来练手，试试把它 CLI 化。&lt;/p&gt;&#xA;&lt;h2 id=&#34;开发流程&#34;&gt;开发流程&lt;/h2&gt;&#xA;&lt;p&gt;第一步肯定是先了解一下 CLI 的开发流程。我现在除了知道应该要用 Node.js 开发，其他几乎是零基础。那就老老实实先上网搜。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;  &#xA;&#xA;&lt;figure&#xA;    class=&#34;image-caption image-zoom-container&#34;&#xA;&gt;&#xA;    &#xA;    &lt;input&#xA;        type=&#34;checkbox&#34;&#xA;        id=&#34;img-17594071&#34;&#xA;        class=&#34;image-zoom-toggle&#34;&#xA;        aria-label=&#34;放大图片：搜索 Node.js CLI&#34;&#xA;    /&gt;&#xA;    &lt;label&#xA;        for=&#34;img-17594071&#34;&#xA;        class=&#34;image-zoom-label&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T12-32-53-351Z.png&#34;&#xA;            alt=&#34;搜索 Node.js CLI&#34;&#xA;            loading=&#34;lazy&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;label&#xA;        for=&#34;img-17594071&#34;&#xA;        class=&#34;image-zoom-overlay&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T12-32-53-351Z.png&#34;&#xA;            alt=&#34;搜索 Node.js CLI&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;figcaption&gt;搜索 Node.js CLI&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;找到了一篇 2019 年的知乎文章：&lt;a href=&#34;https://zhuanlan.zhihu.com/p/38730825&#34;&gt;用Node.js开发一个Command Line Interface (CLI)&lt;/a&gt;。本来我看到这个时间，第一反应是打算划走，但换个思路一想，这么久远的文章还能被我搜到，说不定反而是一篇优质内容，而且标题也非常直白，所以我还是决定仔细看一看。&lt;/p&gt;&#xA;&lt;p&gt;看完之后，大概就知道整个流程是什么样了。和我预想得差不多，确实是用 Node.js 开发，难怪平时网上安装 CLI 的时候都会让我先确保本地有 Node 环境，而且很多工具都是直接用 &lt;code&gt;npx&lt;/code&gt; 起。&lt;/p&gt;&#xA;&lt;p&gt;开发流程大概可以分成下面几步：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;创建 npm 模块&lt;/li&gt;&#xA;&lt;li&gt;增加 bin 入口文件&lt;/li&gt;&#xA;&lt;li&gt;全局安装&lt;/li&gt;&#xA;&lt;li&gt;发布&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;这么一看，整个路径其实还挺清晰的。先把最小可用版本做出来，再往上加子命令就行。&lt;/p&gt;&#xA;&lt;h2 id=&#34;环境配置&#34;&gt;环境配置&lt;/h2&gt;&#xA;&lt;p&gt;先简单配置一下 &lt;a href=&#34;https://nodejs.org/zh-cn&#34;&gt;Node.js&lt;/a&gt; 环境。我平时一般用 &lt;a href=&#34;https://www.nvmnode.com/&#34;&gt;nvm&lt;/a&gt; 管理 Node 版本，毕竟我之前真的因为版本差异排查过很久 bug，这种坑踩过一次就够了。&lt;/p&gt;&#xA;&lt;p&gt;Node 环境配好之后先检查一下：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;  &#xA;&#xA;&lt;figure&#xA;    class=&#34;image-caption image-zoom-container&#34;&#xA;&gt;&#xA;    &#xA;    &lt;input&#xA;        type=&#34;checkbox&#34;&#xA;        id=&#34;img-01659321&#34;&#xA;        class=&#34;image-zoom-toggle&#34;&#xA;        aria-label=&#34;放大图片：本地 Node 环境检查结果&#34;&#xA;    /&gt;&#xA;    &lt;label&#xA;        for=&#34;img-01659321&#34;&#xA;        class=&#34;image-zoom-label&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T11-45-40-376Z.png&#34;&#xA;            alt=&#34;本地 Node 环境检查结果&#34;&#xA;            loading=&#34;lazy&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;label&#xA;        for=&#34;img-01659321&#34;&#xA;        class=&#34;image-zoom-overlay&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T11-45-40-376Z.png&#34;&#xA;            alt=&#34;本地 Node 环境检查结果&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;figcaption&gt;本地 Node 环境检查结果&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;创建 npm 模块，直接执行：&lt;code&gt;npm init&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;  &#xA;&#xA;&lt;figure&#xA;    class=&#34;image-caption image-zoom-container&#34;&#xA;&gt;&#xA;    &#xA;    &lt;input&#xA;        type=&#34;checkbox&#34;&#xA;        id=&#34;img-fb739af8&#34;&#xA;        class=&#34;image-zoom-toggle&#34;&#xA;        aria-label=&#34;放大图片：使用 npm init 初始化项目&#34;&#xA;    /&gt;&#xA;    &lt;label&#xA;        for=&#34;img-fb739af8&#34;&#xA;        class=&#34;image-zoom-label&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T12-36-11-738Z.png&#34;&#xA;            alt=&#34;使用 npm init 初始化项目&#34;&#xA;            loading=&#34;lazy&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;label&#xA;        for=&#34;img-fb739af8&#34;&#xA;        class=&#34;image-zoom-overlay&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T12-36-11-738Z.png&#34;&#xA;            alt=&#34;使用 npm init 初始化项目&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;figcaption&gt;使用 npm init 初始化项目&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;这样就初始化好了整个项目：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;zhlgd-cli&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;武汉理工大学智慧理工大命令行工具&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;homepage&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://github.com/kmoonn/zhlgd-cli#readme&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;bugs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://github.com/kmoonn/zhlgd-cli/issues&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;repository&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;git&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;git+https://github.com/kmoonn/zhlgd-cli.git&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;license&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;MIT&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Kmoon&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;module&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;index.js&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;echo \&amp;#34;Error: no test specified\&amp;#34; &amp;amp;&amp;amp; exit 1&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;type&lt;/code&gt; 这里选择 &lt;code&gt;module&lt;/code&gt;，因为新版 ES 模块化用的是 &lt;code&gt;import/export&lt;/code&gt;，现在做 CLI 基本都会用到。&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;commonjs&lt;/code&gt; 则是老版 Node.js 模块化，写法是 &lt;code&gt;require()&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;接着在 &lt;code&gt;package.json&lt;/code&gt; 里增加 &lt;code&gt;bin&lt;/code&gt; 字段：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;bin&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;zhlgd&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;./bin/index.js&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里也顺便记一下：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;zhlgd-cli&lt;/code&gt; 是项目名 / 包名&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;zhlgd&lt;/code&gt; 才是最终在终端里执行的命令&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;在项目根目录新建一个 &lt;code&gt;bin&lt;/code&gt; 文件夹，并在里面新建一个 &lt;code&gt;index.js&lt;/code&gt; 文件。&lt;/p&gt;&#xA;&lt;p&gt;文件结构长这样：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;zhlgd-cli/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── bin/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── index.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── package.json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编写 &lt;code&gt;bin/index.js&lt;/code&gt; 代码。&lt;/p&gt;&#xA;&lt;p&gt;第一行必须写：&lt;code&gt;#!/usr/bin/env node&lt;/code&gt;，意思是告诉系统用 Node 来运行这个脚本。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#!/usr/bin/env node&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 你的命令行逻辑开始&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;✅ 智慧理工大命令行工具启动成功！&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;👉 命令：zhlgd&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后全局安装，把命令链接到本地环境。&lt;/p&gt;&#xA;&lt;p&gt;在项目根目录执行：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm link&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;和下面图片一样，基本就说明成功了。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;  &#xA;&#xA;&lt;figure&#xA;    class=&#34;image-caption image-zoom-container&#34;&#xA;&gt;&#xA;    &#xA;    &lt;input&#xA;        type=&#34;checkbox&#34;&#xA;        id=&#34;img-6c8a91bc&#34;&#xA;        class=&#34;image-zoom-toggle&#34;&#xA;        aria-label=&#34;放大图片：npm link 成功后的终端输出&#34;&#xA;    /&gt;&#xA;    &lt;label&#xA;        for=&#34;img-6c8a91bc&#34;&#xA;        class=&#34;image-zoom-label&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T11-45-00-454Z.png&#34;&#xA;            alt=&#34;npm link 成功后的终端输出&#34;&#xA;            loading=&#34;lazy&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;label&#xA;        for=&#34;img-6c8a91bc&#34;&#xA;        class=&#34;image-zoom-overlay&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T11-45-00-454Z.png&#34;&#xA;            alt=&#34;npm link 成功后的终端输出&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;figcaption&gt;npm link 成功后的终端输出&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;这个阶段主要是本地开发调试方便。我后面把它正式发到了 npm 上，所以现在除了 &lt;code&gt;npm link&lt;/code&gt; 这种本地联调方式之外，也可以直接下载安装：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm i zhlgd-cli&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;能走到这一步，我自己其实还挺开心的。因为它说明这个项目已经不只是我本地电脑上的一个小玩具，而是真的变成了一个别人也能装下来体验的 CLI。&lt;/p&gt;&#xA;&lt;p&gt;然后测试一下我们的命令。新开一个终端输入 &lt;code&gt;zhlgd&lt;/code&gt;，就可以直接看到输出：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;  &#xA;&#xA;&lt;figure&#xA;    class=&#34;image-caption image-zoom-container&#34;&#xA;&gt;&#xA;    &#xA;    &lt;input&#xA;        type=&#34;checkbox&#34;&#xA;        id=&#34;img-3b0c4f1c&#34;&#xA;        class=&#34;image-zoom-toggle&#34;&#xA;        aria-label=&#34;放大图片：首次运行 zhlgd 命令的输出&#34;&#xA;    /&gt;&#xA;    &lt;label&#xA;        for=&#34;img-3b0c4f1c&#34;&#xA;        class=&#34;image-zoom-label&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T11-44-22-324Z.png&#34;&#xA;            alt=&#34;首次运行 zhlgd 命令的输出&#34;&#xA;            loading=&#34;lazy&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;label&#xA;        for=&#34;img-3b0c4f1c&#34;&#xA;        class=&#34;image-zoom-overlay&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T11-44-22-324Z.png&#34;&#xA;            alt=&#34;首次运行 zhlgd 命令的输出&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;figcaption&gt;首次运行 zhlgd 命令的输出&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;到这一步，其实最小可用 CLI 就已经搭起来了。后面要做的，就是继续往里面塞真正有用的子命令。&lt;/p&gt;&#xA;&lt;h2 id=&#34;zhlgd-help&#34;&gt;zhlgd help&lt;/h2&gt;&#xA;&lt;p&gt;接下来先完成第一个最简单的命令，也是几乎每个 CLI 都会有的命令：&lt;code&gt;help&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;这里认识一个新东西，官方很常见的 CLI 开发框架：&lt;a href=&#34;https://github.com/tj/commander.js&#34;&gt;commander&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;p&gt;先安装一下依赖：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install commander&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;写着写着我发现，&lt;code&gt;help&lt;/code&gt; 其实根本不用自己手搓，框架已经给你做好了。&lt;/p&gt;&#xA;&lt;p&gt;下面是使用 commander 改造后的入口文件 &lt;code&gt;index.js&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ch&#34;&gt;#!/usr/bin/env node&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 导入 commander 库，用于处理命令行&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Command&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;commander&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;program&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 配置 CLI 基础信息&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;program&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;zhlgd&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;1.0.0&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;-v, --version&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;一个基于 Node.js 开发的智慧理工大 CLI 工具&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 解析命令行输入的参数&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;program&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;argv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这一步虽然简单，但我感觉挺关键。因为从这里开始，这个项目才不只是一个“能打印一句话的脚本”，而是开始往真正的 CLI 工具靠了。&lt;/p&gt;&#xA;&lt;p&gt;运行一下看看效果：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;  &#xA;&#xA;&lt;figure&#xA;    class=&#34;image-caption image-zoom-container&#34;&#xA;&gt;&#xA;    &#xA;    &lt;input&#xA;        type=&#34;checkbox&#34;&#xA;        id=&#34;img-d0bc9da6&#34;&#xA;        class=&#34;image-zoom-toggle&#34;&#xA;        aria-label=&#34;放大图片：zhlgd help 命令输出&#34;&#xA;    /&gt;&#xA;    &lt;label&#xA;        for=&#34;img-d0bc9da6&#34;&#xA;        class=&#34;image-zoom-label&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T12-31-14-420Z.png&#34;&#xA;            alt=&#34;zhlgd help 命令输出&#34;&#xA;            loading=&#34;lazy&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;label&#xA;        for=&#34;img-d0bc9da6&#34;&#xA;        class=&#34;image-zoom-overlay&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-08T12-31-14-420Z.png&#34;&#xA;            alt=&#34;zhlgd help 命令输出&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;figcaption&gt;zhlgd help 命令输出&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;zhlgd-login&#34;&gt;zhlgd login&lt;/h2&gt;&#xA;&lt;p&gt;下面开始啃一个硬骨头：&lt;strong&gt;登录&lt;/strong&gt;。毕竟只有先登录，后面的事情才做得下去。&lt;/p&gt;&#xA;&lt;p&gt;智慧理工大的登录是单点登录，大概流程如下：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;用户输入账号、密码 → 点击登录&lt;/li&gt;&#xA;&lt;li&gt;前端校验：账号/密码不能为空&lt;/li&gt;&#xA;&lt;li&gt;记住密码：写入 Cookie（加密存储）&lt;/li&gt;&#xA;&lt;li&gt;向后端请求 &lt;strong&gt;RSA 公钥&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;使用公钥 &lt;strong&gt;加密账号、密码&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;把加密后的账号密码塞进表单隐藏域&lt;/li&gt;&#xA;&lt;li&gt;自动提交登录表单 → 后端验证 → 登录成功 / 失败&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;每次登录时，前端都会先请求后端获取 RSA 加密公钥：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 获取 key&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ajax&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;rsa?skipWechat=true&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;dataType&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;json&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;success&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;encrypt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;JSEncrypt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;encrypt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;setPublicKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;publicKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;#ul&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;encrypt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;encrypt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;#pl&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;encrypt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;encrypt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;#loginForm&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;submit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;网页里这一步看起来很自然，因为浏览器已经帮你把表单提交、跳转、Cookie 处理这些事情都包掉了。但放到 CLI 里，这些步骤就得自己一层层补出来。&lt;/p&gt;&#xA;&lt;p&gt;也就是说，在 CLI 里，登录这件事至少要做下面几步：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;请求 RSA 公钥&lt;/li&gt;&#xA;&lt;li&gt;用公钥加密账号和密码&lt;/li&gt;&#xA;&lt;li&gt;提交登录请求，拿到 ST ticket&lt;/li&gt;&#xA;&lt;li&gt;根据 ticket 继续跟重定向，换取完整 Cookie&lt;/li&gt;&#xA;&lt;li&gt;把 Cookie 保存到本地，后续命令统一复用&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;对用户名和密码加密之后，再提交登录请求获取 ST ticket，接着顺着重定向把完整 Cookie 换回来，并保存到本地，后续都拿这份 Cookie 作为鉴权使用：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 5. 用 ST 换取完整 Cookie&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;st&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;extractTicket&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;loginResp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;location&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;followRedirects&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;SERVICE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;?ticket=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;st&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cookieJar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;commonHeaders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 6. 保存&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;saveCookies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cookieJar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里我自己的体感是，&lt;strong&gt;登录这一关是整个 CLI 最麻烦的部分&lt;/strong&gt;。因为一旦 Cookie 这套机制没打通，后面所有需要鉴权的接口都跑不起来；但只要这一关打通了，后续子命令反而会顺很多，本质上就变成“带上鉴权去请求接口，再把结果整理一下输出”。&lt;/p&gt;&#xA;&lt;p&gt;下面演示一下登录过程。这里为了安全，密码输入做了隐藏处理：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;  &#xA;&#xA;&lt;figure&#xA;    class=&#34;image-caption image-zoom-container&#34;&#xA;&gt;&#xA;    &#xA;    &lt;input&#xA;        type=&#34;checkbox&#34;&#xA;        id=&#34;img-4b7e8383&#34;&#xA;        class=&#34;image-zoom-toggle&#34;&#xA;        aria-label=&#34;放大图片：zhlgd login 命令的隐藏密码输入效果&#34;&#xA;    /&gt;&#xA;    &lt;label&#xA;        for=&#34;img-4b7e8383&#34;&#xA;        class=&#34;image-zoom-label&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-14T02-16-43-242Z.png&#34;&#xA;            alt=&#34;zhlgd login 命令的隐藏密码输入效果&#34;&#xA;            loading=&#34;lazy&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;label&#xA;        for=&#34;img-4b7e8383&#34;&#xA;        class=&#34;image-zoom-overlay&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-14T02-16-43-242Z.png&#34;&#xA;            alt=&#34;zhlgd login 命令的隐藏密码输入效果&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;figcaption&gt;zhlgd login 命令的隐藏密码输入效果&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;到这里，&lt;code&gt;login&lt;/code&gt; 命令就算是跑通了。现在这个项目也已经正式发到了 npm 上，直接执行下面这条命令就可以安装：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm i zhlgd-cli&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;  &#xA;&#xA;&lt;figure&#xA;    class=&#34;image-caption image-zoom-container&#34;&#xA;&gt;&#xA;    &#xA;    &lt;input&#xA;        type=&#34;checkbox&#34;&#xA;        id=&#34;img-6ae3548a&#34;&#xA;        class=&#34;image-zoom-toggle&#34;&#xA;        aria-label=&#34;放大图片：npm&#34;&#xA;    /&gt;&#xA;    &lt;label&#xA;        for=&#34;img-6ae3548a&#34;&#xA;        class=&#34;image-zoom-label&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-14T03-20-20-659Z.png&#34;&#xA;            alt=&#34;npm&#34;&#xA;            loading=&#34;lazy&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;label&#xA;        for=&#34;img-6ae3548a&#34;&#xA;        class=&#34;image-zoom-overlay&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-14T03-20-20-659Z.png&#34;&#xA;            alt=&#34;npm&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;figcaption&gt;npm&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;所以如果你只是想直接上手体验，而不是自己从头本地联调，走 npm 安装会更方便。具体实现细节如果感兴趣，也可以直接看源码：&lt;a href=&#34;https://github.com/kmoonn/zhlgd-cli&#34;&gt;zhlgd-cli GitHub 仓库&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;zhlgd-msg--news&#34;&gt;zhlgd msg &amp;amp; news&lt;/h2&gt;&#xA;&lt;p&gt;登录搞定之后，我先做了两个最基础、也最容易验证成果的功能：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;zhlgd msg&lt;/code&gt;：查看我的消息&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;zhlgd news&lt;/code&gt;：查看校园新闻&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;之所以先做这两个，是因为它们很适合拿来验证整条链路是不是已经通了：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;登录是否真的拿到了可用 Cookie&lt;/li&gt;&#xA;&lt;li&gt;Cookie 是否已经被正确保存&lt;/li&gt;&#xA;&lt;li&gt;后续子命令是否能稳定复用这套鉴权能力&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;实现上其实没有想象中那么玄学，核心就是把两个接口分别转成 JavaScript 请求：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;apiRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;BASE_URL&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;/tp_up_new/up/newhome/getFpMsgList`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MSG_TYPE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;3&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;apiRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;BASE_URL&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;/tp_up_new/up/newhome/getNewsNotice`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;channel&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;13962&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;真正关键的地方，还是请求时要把登录成功后保存下来的 Cookie 带上：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 带鉴权的 API 请求，未登录时自动提示&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;apiRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;isLoggedIn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;⚠️  未登录，请先执行 zhlgd login&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cookies&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;loadCookies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;resp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;axios&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;API_HEADERS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nx&#34;&gt;cookie&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cookieHeader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cookies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s1&#34;&gt;&amp;#39;User-Agent&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UA&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;resp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所以说，前面把 &lt;code&gt;login&lt;/code&gt; 打通之后，后面很多功能的开发思路就都统一了：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;找到对应接口&lt;/li&gt;&#xA;&lt;li&gt;搞清楚请求参数&lt;/li&gt;&#xA;&lt;li&gt;带上登录态发请求&lt;/li&gt;&#xA;&lt;li&gt;把返回结果整理成终端里更适合看的样子&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;下面测试一下实际效果：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;  &#xA;&#xA;&lt;figure&#xA;    class=&#34;image-caption image-zoom-container&#34;&#xA;&gt;&#xA;    &#xA;    &lt;input&#xA;        type=&#34;checkbox&#34;&#xA;        id=&#34;img-25b81c94&#34;&#xA;        class=&#34;image-zoom-toggle&#34;&#xA;        aria-label=&#34;放大图片：zhlgd msg 命令输出效果&#34;&#xA;    /&gt;&#xA;    &lt;label&#xA;        for=&#34;img-25b81c94&#34;&#xA;        class=&#34;image-zoom-label&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-14T02-22-55-918Z.png&#34;&#xA;            alt=&#34;zhlgd msg 命令输出效果&#34;&#xA;            loading=&#34;lazy&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;label&#xA;        for=&#34;img-25b81c94&#34;&#xA;        class=&#34;image-zoom-overlay&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-14T02-22-55-918Z.png&#34;&#xA;            alt=&#34;zhlgd msg 命令输出效果&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;figcaption&gt;zhlgd msg 命令输出效果&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;  &#xA;&#xA;&lt;figure&#xA;    class=&#34;image-caption image-zoom-container&#34;&#xA;&gt;&#xA;    &#xA;    &lt;input&#xA;        type=&#34;checkbox&#34;&#xA;        id=&#34;img-64a69e0d&#34;&#xA;        class=&#34;image-zoom-toggle&#34;&#xA;        aria-label=&#34;放大图片：zhlgd news 命令输出效果&#34;&#xA;    /&gt;&#xA;    &lt;label&#xA;        for=&#34;img-64a69e0d&#34;&#xA;        class=&#34;image-zoom-label&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-14T02-23-11-367Z.png&#34;&#xA;            alt=&#34;zhlgd news 命令输出效果&#34;&#xA;            loading=&#34;lazy&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;label&#xA;        for=&#34;img-64a69e0d&#34;&#xA;        class=&#34;image-zoom-overlay&#34;&#xA;    &gt;&#xA;        &lt;img&#xA;            src=&#34;https://cdn.kmoon.fun/2026/2026-06-14T02-23-11-367Z.png&#34;&#xA;            alt=&#34;zhlgd news 命令输出效果&#34;&#xA;            class=&#34;zoomable-image&#34;&#xA;        /&gt;&#xA;    &lt;/label&gt;&#xA;    &#xA;    &lt;figcaption&gt;zhlgd news 命令输出效果&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;看到这里，我自己其实还挺有成就感的。因为它已经不再只是一个能打印几行字的 demo，而是真的具备了“登录 -&amp;gt; 带鉴权请求 -&amp;gt; 输出结果”这一整套能力。&lt;/p&gt;&#xA;&lt;h2 id=&#34;写在最后&#34;&gt;写在最后&lt;/h2&gt;&#xA;&lt;p&gt;这次折腾 &lt;code&gt;zhlgd-cli&lt;/code&gt;，对我来说最有意思的地方，不是把 &lt;code&gt;help&lt;/code&gt; 命令跑起来，而是第一次比较完整地把“网页里浏览器偷偷帮你做的事情”，一点点搬到了 CLI 里。&lt;/p&gt;&#xA;&lt;p&gt;目前这个 CLI 至少已经把最难的登录流程打通了，也做出了 &lt;code&gt;msg&lt;/code&gt; 和 &lt;code&gt;news&lt;/code&gt; 这两个能实际用起来的子命令。后面如果继续往下做，其实就可以顺着这条路继续扩，比如把更多查询类功能搬进终端，甚至再往前走一步，给 Agent 直接调用。&lt;/p&gt;&#xA;&lt;p&gt;总之，这次尝试让我更直观地感受到一件事：CLI 这种东西，看起来很“黑框”，但一旦场景选对了，真的会很顺手。尤其是在 AI 时代，它不只是给人用的工具，很多时候也天然适合给 Agent 干活。&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>
