<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/scripts/pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:h="http://www.w3.org/TR/html4/"><channel><title>StoicNeko&apos;s Blog</title><description>Stay hungry, stay foolish</description><link>https://blogs-6hn.pages.dev</link><item><title>解决 kind load docker-image 多平台镜像导入失败</title><link>https://blogs-6hn.pages.dev/blog/kind-load-docker-image-multiplatform-fix</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/kind-load-docker-image-multiplatform-fix</guid><description>kind load docker-image 报 content digest not found？根因是多平台 manifest，不是 containerd image store。用 crane 拉单平台镜像可以彻底解决。</description><pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;解决 kind load docker-image 多平台镜像导入失败&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;kind load docker-image&lt;/code&gt; 导入本地镜像到 Kind 集群时，报了一个莫名其妙的错误：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ kind load docker-image prom/prometheus --name kind
ERROR: failed to load image: command &quot;docker exec --privileged -i kind-control-plane ctr --namespace=k8s.io images import --all-platforms --digests --snapshotter=overlayfs -&quot; failed with error: exit status 1
Command Output: ctr: content digest sha256:ce0e992cc801a3e5a8595e18e5a78748d69794933c529bf4a2dc14d67ac1d85c: not found
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;网上很多文章说这是 Docker Desktop 启用 containerd image store 导致的。&lt;strong&gt;不完全对。&lt;/strong&gt; 真正的原因是多平台 manifest。&lt;/p&gt;
&lt;h2&gt;环境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;WSL2 (Arch Linux) + Docker Desktop 29.2.1&lt;/li&gt;
&lt;li&gt;Kind v0.31.0&lt;/li&gt;
&lt;li&gt;平台：linux/amd64&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;根因分析&lt;/h2&gt;
&lt;p&gt;先看看 &lt;code&gt;prom/prometheus&lt;/code&gt; 的 manifest：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ docker manifest inspect prom/prometheus:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;schemaVersion&quot;: 2,
  &quot;mediaType&quot;: &quot;application/vnd.docker.distribution.manifest.list.v2+json&quot;,
  &quot;manifests&quot;: [
    {
      &quot;digest&quot;: &quot;sha256:82bb24e3...&quot;,
      &quot;platform&quot;: { &quot;architecture&quot;: &quot;amd64&quot;, &quot;os&quot;: &quot;linux&quot; }
    },
    {
      &quot;digest&quot;: &quot;sha256:ce0e992cc801a3e5a8595e18e5a78748d69794933c529bf4a2dc14d67ac1d85c&quot;,
      &quot;platform&quot;: { &quot;architecture&quot;: &quot;arm64&quot;, &quot;os&quot;: &quot;linux&quot; }
    },
    {
      &quot;digest&quot;: &quot;sha256:b355043d...&quot;,
      &quot;platform&quot;: { &quot;architecture&quot;: &quot;arm&quot;, &quot;os&quot;: &quot;linux&quot;, &quot;variant&quot;: &quot;v7&quot; }
    },
    {
      &quot;digest&quot;: &quot;sha256:dad74217...&quot;,
      &quot;platform&quot;: { &quot;architecture&quot;: &quot;ppc64le&quot;, &quot;os&quot;: &quot;linux&quot; }
    },
    {
      &quot;digest&quot;: &quot;sha256:68b8d469...&quot;,
      &quot;platform&quot;: { &quot;architecture&quot;: &quot;riscv64&quot;, &quot;os&quot;: &quot;linux&quot; }
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意报错里的 &lt;code&gt;sha256:ce0e992cc801...&lt;/code&gt;——正好是 &lt;strong&gt;arm64&lt;/strong&gt; 平台的 digest。&lt;/p&gt;
&lt;p&gt;整个链条是这样的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;docker pull prom/prometheus&lt;/code&gt; 只下载当前平台（amd64）的 layers，但本地保留了完整的 manifest list（包含 arm64、armv7、ppc64le、riscv64 的引用）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kind load docker-image&lt;/code&gt; 内部执行 &lt;code&gt;docker save&lt;/code&gt; 导出镜像，再通过 &lt;code&gt;docker exec&lt;/code&gt; 管道给 Kind 节点里的 &lt;code&gt;ctr images import --all-platforms&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctr&lt;/code&gt; 解析 manifest list，尝试导入所有平台，找 arm64 的 content layer 时发现不存在——报错&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;关键&lt;/strong&gt;：&lt;code&gt;docker save&lt;/code&gt; 导出的 tar 里确实只有 amd64 的 layers，但 manifest list 仍然引用了其他平台。&lt;code&gt;ctr --all-platforms&lt;/code&gt; 不会跳过缺失的平台，而是直接报错。&lt;/p&gt;
&lt;h3&gt;为什么 docker save + kind load image-archive 也不行？&lt;/h3&gt;
&lt;p&gt;你可能想到了先 &lt;code&gt;docker save&lt;/code&gt; 再 &lt;code&gt;kind load image-archive&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker save prom/prometheus -o /tmp/prometheus.tar
kind load image-archive /tmp/prometheus.tar --name kind
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同样会失败，原因一样——&lt;code&gt;docker save&lt;/code&gt; 生成的 tar 包含了多平台 manifest list，&lt;code&gt;kind load image-archive&lt;/code&gt; 内部也是调用 &lt;code&gt;ctr images import --all-platforms&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;为什么 docker pull --platform 也不行？&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker pull --platform linux/amd64 prom/prometheus
kind load docker-image prom/prometheus --name kind
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Docker 拉取时虽然只下载了 amd64 的 layers，但本地存储仍然保留了 manifest list 索引。&lt;code&gt;docker save&lt;/code&gt; 导出时这个索引会被带上，&lt;code&gt;ctr&lt;/code&gt; 照样尝试解析所有平台。&lt;/p&gt;
&lt;h2&gt;解决方案：crane 拉单平台镜像&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/go-containerregistry/tree/main/cmd/crane&quot;&gt;crane&lt;/a&gt; 是 Google 开源的容器镜像工具，可以精确拉取单平台镜像，生成的 tar 不包含多平台 manifest list。&lt;/p&gt;
&lt;h3&gt;安装 crane&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;go install github.com/google/go-containerregistry/cmd/crane@latest
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;拉取并导入&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;crane pull --platform linux/amd64 prom/prometheus /tmp/prometheus-amd64.tar
kind load image-archive /tmp/prometheus-amd64.tar --name kind
rm /tmp/prometheus-amd64.tar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;没有报错，导入成功。&lt;/p&gt;
&lt;h3&gt;验证&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ docker exec kind-control-plane crictl images | grep prometheus
docker.io/prom/prometheus    latest    4a61322ac110    143MB
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;对比&lt;/h2&gt;
&lt;p&gt;| 方法 | 结果 | 原因 |
|------|------|------|
| &lt;code&gt;kind load docker-image&lt;/code&gt; | 失败 | &lt;code&gt;docker save&lt;/code&gt; 带多平台 manifest |
| &lt;code&gt;docker save&lt;/code&gt; + &lt;code&gt;kind load image-archive&lt;/code&gt; | 失败 | 同上 |
| &lt;code&gt;docker pull --platform&lt;/code&gt; + &lt;code&gt;kind load&lt;/code&gt; | 失败 | 本地仍保留 manifest list |
| &lt;code&gt;crane pull --platform&lt;/code&gt; + &lt;code&gt;kind load image-archive&lt;/code&gt; | 成功 | 只包含单平台 manifest |&lt;/p&gt;
&lt;h2&gt;写个 helper 函数&lt;/h2&gt;
&lt;p&gt;如果经常需要导入镜像，可以加到 shell 配置里：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# ~/.config/fish/functions/kind-load.fish (Fish Shell)
function kind-load
    set -l image $argv[1]
    set -l cluster (test (count $argv) -ge 2; and echo $argv[2]; or echo &quot;kind&quot;)
    set -l tmpfile (mktemp /tmp/kind-load-XXXXXX.tar)

    echo &quot;Pulling $image (linux/amd64)...&quot;
    crane pull --platform linux/amd64 $image $tmpfile
    and echo &quot;Loading into kind cluster &apos;$cluster&apos;...&quot;
    and kind load image-archive $tmpfile --name $cluster
    rm -f $tmpfile
end
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# ~/.bashrc 或 ~/.zshrc (Bash/Zsh)
kind-load() {
    local image=&quot;$1&quot;
    local cluster=&quot;${2:-kind}&quot;
    local tmpfile
    tmpfile=$(mktemp /tmp/kind-load-XXXXXX.tar)

    echo &quot;Pulling $image (linux/amd64)...&quot;
    crane pull --platform linux/amd64 &quot;$image&quot; &quot;$tmpfile&quot; \
        &amp;#x26;&amp;#x26; echo &quot;Loading into kind cluster &apos;$cluster&apos;...&quot; \
        &amp;#x26;&amp;#x26; kind load image-archive &quot;$tmpfile&quot; --name &quot;$cluster&quot;
    rm -f &quot;$tmpfile&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用法：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kind-load prom/prometheus        # 默认 kind 集群
kind-load nginx my-cluster       # 指定集群名
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;这个问题跟操作系统有关吗？&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;无关。&lt;/strong&gt; macOS、Linux、WSL2 都会遇到。这是 Docker 存储多平台镜像的方式和 Kind 导入逻辑之间的不兼容。macOS 上的 Docker Desktop 行为完全一样。Apple Silicon 的 Mac 甚至更容易触发，因为默认拉 arm64 但 manifest 里还引用了 amd64。&lt;/p&gt;
&lt;p&gt;这是 Kind 的已知问题：&lt;a href=&quot;https://github.com/kubernetes-sigs/kind/issues/3053&quot;&gt;kubernetes-sigs/kind#3053&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;kind load docker-image&lt;/code&gt; 失败的核心原因：&lt;strong&gt;Docker 本地保留了多平台 manifest list，但只有当前平台的 layers。Kind 用 &lt;code&gt;ctr --all-platforms&lt;/code&gt; 导入时找不到其他平台的 content digest。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;解决方案：用 &lt;code&gt;crane pull --platform&lt;/code&gt; 拉纯粹的单平台镜像，绕过 Docker 的多平台 manifest。&lt;/p&gt;</content:encoded></item><item><title>解决 Kind 在 WSL2 + Docker Desktop 下无法拉取镜像的代理问题</title><link>https://blogs-6hn.pages.dev/blog/kind-proxy-wsl2-docker-desktop</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/kind-proxy-wsl2-docker-desktop</guid><description>Kind 节点里 localhost 不是你的 localhost。记录在 WSL2 + Docker Desktop 环境中，Kind 容器无法通过代理拉取镜像的排查过程和最终解决方案。</description><pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;解决 Kind 在 WSL2 + Docker Desktop 下无法拉取镜像的代理问题&lt;/h1&gt;
&lt;p&gt;在 WSL2 里跑 Kind，结果 Pod 一直 &lt;code&gt;ImagePullBackOff&lt;/code&gt;。代理明明在 Windows 上跑着，&lt;code&gt;docker pull&lt;/code&gt; 也能用，Kind 就是拉不到镜像。折腾了好几个小时，记录一下完整的排查过程。&lt;/p&gt;
&lt;h2&gt;环境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;WSL2 (Arch Linux) on Windows&lt;/li&gt;
&lt;li&gt;Docker Desktop (WSL2 backend)&lt;/li&gt;
&lt;li&gt;Kind (Kubernetes in Docker)&lt;/li&gt;
&lt;li&gt;Sparkle（Clash 内核的代理客户端），监听 &lt;code&gt;7890&lt;/code&gt; 端口&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;症状&lt;/h2&gt;
&lt;p&gt;创建 Kind 集群后，部署一个简单的 nginx Pod：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl run nginx --image=nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pod 一直卡在 &lt;code&gt;ImagePullBackOff&lt;/code&gt;。&lt;code&gt;describe&lt;/code&gt; 看到的错误：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Failed to pull image &quot;nginx:latest&quot;: ... dial tcp [::1]:7890: connect: connection refused
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键信息：&lt;strong&gt;&lt;code&gt;[::1]:7890&lt;/code&gt;&lt;/strong&gt;——Kind 节点在尝试连接 IPv6 的 localhost，但没有任何东西在容器内监听这个地址。&lt;/p&gt;
&lt;h2&gt;排查过程&lt;/h2&gt;
&lt;h3&gt;第一个坑：代理只监听 127.0.0.1&lt;/h3&gt;
&lt;p&gt;在 Windows 上检查代理端口：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;netstat -ano | findstr 7890
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一开始看到的是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TCP    127.0.0.1:7890    0.0.0.0:0    LISTENING    40024
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;只监听了 &lt;code&gt;127.0.0.1&lt;/code&gt;，也就是说只有 Windows 本机能连，WSL2 和 Docker 容器都连不上。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：Sparkle 有两个&quot;允许局域网&quot;开关——一个在 Sub-Store 面板里（没用），一个在&lt;strong&gt;内核设置&lt;/strong&gt;里。需要打开&lt;strong&gt;内核级别&lt;/strong&gt;的&quot;允许局域网&quot;选项。&lt;/p&gt;
&lt;p&gt;打开后确认：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;netstat -ano | findstr 7890
# TCP    0.0.0.0:7890    0.0.0.0:0    LISTENING
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;0.0.0.0&lt;/code&gt; 表示监听所有网络接口，这才对。&lt;/p&gt;
&lt;h3&gt;第二个坑：Kind 节点里的 localhost 不是宿主机&lt;/h3&gt;
&lt;p&gt;代理开放了局域网访问，但 Kind 节点内的 &lt;code&gt;HTTP_PROXY=http://localhost:7890&lt;/code&gt; 仍然不工作。&lt;/p&gt;
&lt;p&gt;这是因为 Kind 节点本质上是 &lt;strong&gt;Docker 容器&lt;/strong&gt;。容器内的 &lt;code&gt;localhost&lt;/code&gt; 指向的是容器自身的网络命名空间，不是 Windows 宿主机。&lt;/p&gt;
&lt;p&gt;需要找到一个从容器内部能到达宿主机的地址。&lt;/p&gt;
&lt;h3&gt;尝试过的地址&lt;/h3&gt;
&lt;p&gt;| 地址 | 结果 | 原因 |
|------|------|------|
| &lt;code&gt;localhost:7890&lt;/code&gt; | connection refused | 指向容器自身 |
| &lt;code&gt;172.22.0.1:7890&lt;/code&gt; (网关 IP) | connection refused | 这是 Docker 虚拟网络的网关，不是 Windows 主机 |
| &lt;code&gt;host.docker.internal:7890&lt;/code&gt; | 成功 | Docker Desktop 提供的特殊 DNS，解析到宿主机 |&lt;/p&gt;
&lt;p&gt;在 Kind 节点里验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker exec kind-control-plane getent hosts host.docker.internal
# 192.168.65.254  host.docker.internal
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解决方案&lt;/h2&gt;
&lt;p&gt;两步：&lt;/p&gt;
&lt;h3&gt;1. Sparkle 内核开启&quot;允许局域网&quot;&lt;/h3&gt;
&lt;p&gt;打开 Sparkle → 内核设置（不是 Sub-Store）→ 允许局域网 → 开启。&lt;/p&gt;
&lt;p&gt;确认代理监听在 &lt;code&gt;0.0.0.0:7890&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;2. 创建 Kind 集群时使用 host.docker.internal&lt;/h3&gt;
&lt;p&gt;Kind 会自动读取宿主机的 &lt;code&gt;HTTP_PROXY&lt;/code&gt; / &lt;code&gt;HTTPS_PROXY&lt;/code&gt; / &lt;code&gt;NO_PROXY&lt;/code&gt; 环境变量，并注入到节点中。所以创建集群时，把代理地址指向 &lt;code&gt;host.docker.internal&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;HTTP_PROXY=http://host.docker.internal:7890 \
HTTPS_PROXY=http://host.docker.internal:7890 \
NO_PROXY=localhost,127.0.0.1,::1,10.96.0.0/16,10.244.0.0/16,kind-control-plane,.svc,.svc.cluster,.svc.cluster.local \
kind create cluster
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建完成后验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker exec kind-control-plane crictl pull nginx:latest
# Image is up to date for sha256:...
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;已有集群怎么办？&lt;/h3&gt;
&lt;p&gt;如果不想重建集群，可以直接修改运行中的 Kind 节点：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker exec kind-control-plane bash -c &apos;
mkdir -p /etc/systemd/system/containerd.service.d
cat &gt; /etc/systemd/system/containerd.service.d/http-proxy.conf &amp;#x3C;&amp;#x3C;EOF
[Service]
Environment=&quot;HTTP_PROXY=http://host.docker.internal:7890&quot;
Environment=&quot;HTTPS_PROXY=http://host.docker.internal:7890&quot;
Environment=&quot;NO_PROXY=localhost,127.0.0.1,::1,10.96.0.0/16,10.244.0.0/16,kind-control-plane,.svc,.svc.cluster,.svc.cluster.local&quot;
EOF
systemctl daemon-reload
systemctl restart containerd
&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;另一个坑：kind load docker-image 失败&lt;/h2&gt;
&lt;p&gt;在排查代理的过程中，我还尝试了先用 Docker 拉镜像再导入 Kind：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker pull nginx:latest
kind load docker-image nginx:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果报错：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ctr: content digest sha256:...: not found
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这其实不是 containerd image store 的问题，而是 Docker 本地保留了多平台 manifest list，但只有当前平台的 layers，&lt;code&gt;kind load&lt;/code&gt; 内部的 &lt;code&gt;ctr --all-platforms&lt;/code&gt; 找不到其他平台的 content digest 就报错了。详细分析和解决方案见 &lt;a href=&quot;/blog/kind-load-docker-image-multiplatform-fix&quot;&gt;kind load docker-image 多平台镜像导入失败&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;| 问题 | 根因 | 解决 |
|------|------|------|
| 代理连不上 | Sparkle 只监听 127.0.0.1 | 内核设置开启&quot;允许局域网&quot; |
| Kind 节点连不上代理 | 容器内 localhost ≠ 宿主机 | 使用 &lt;code&gt;host.docker.internal&lt;/code&gt; |
| kind load 失败 | containerd image store 兼容性 | 让 Kind 节点直接拉镜像 |&lt;/p&gt;
&lt;p&gt;核心就一句话：&lt;strong&gt;Kind 节点是 Docker 容器，容器里的 localhost 是它自己，要用 &lt;code&gt;host.docker.internal&lt;/code&gt; 才能连到 Windows 宿主机上的代理。&lt;/strong&gt;&lt;/p&gt;</content:encoded></item><item><title>用 NoneBot2 + NapCat 做一个 QQ 点歌机器人</title><link>https://blogs-6hn.pages.dev/blog/nonebot-diange-qq-bot</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/nonebot-diange-qq-bot</guid><description>从零搭建一个 QQ 点歌 bot：私聊发 #歌名 就能收到音乐卡片。记录 NapCat 部署、双源搜索、自发消息路由等实际踩过的坑。</description><pubDate>Wed, 18 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;用 NoneBot2 + NapCat 做一个 QQ 点歌机器人&lt;/h1&gt;
&lt;p&gt;想在 QQ 里用歌代替文字——发 &lt;code&gt;#我爱你&lt;/code&gt;，对方收到一张《我爱你》的音乐卡片。听起来很简单，实际做下来踩了不少坑。&lt;/p&gt;
&lt;h2&gt;整体架构&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;用户发送: #我爱你
       │
       ▼
┌──────────────┐   OneBot v11 WS   ┌──────────────────┐
│  NapCat      │ ◄────────────────► │    NoneBot2      │
│  (QQ 协议端)  │                    │  plugins/        │
└──────────────┘                    │  └─ music_search │
                                    └──────────────────┘
                                           │
                                    ┌──────┴──────┐
                                    ▼             ▼
                              网易云音乐API   QQ音乐API
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;NapCat 负责 QQ 登录和收发消息，NoneBot2 负责业务逻辑，两者通过 WebSocket 通信。搜索优先走网易云，搜不到 fallback 到 QQ 音乐。&lt;/p&gt;
&lt;h2&gt;部署 NapCat&lt;/h2&gt;
&lt;p&gt;NapCat 是基于 NTQQ 的第三方 QQ 协议端，支持 Docker 部署。WSL2 下直接跑：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker run -d --name napcat \
  -p 3001:3001 -p 6099:6099 \
  mlikiowa/napcat-docker:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;国内 Docker Hub 被墙，用 DaoCloud 镜像：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker run -d --name napcat \
  -p 3001:3001 -p 6099:6099 \
  m.daocloud.io/docker.io/mlikiowa/napcat-docker:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动后访问 &lt;code&gt;http://127.0.0.1:6099/webui&lt;/code&gt;，token 在日志里：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker logs napcat | grep token
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;扫码登录后，在 NapCat 配置里需要改两个东西：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;WebSocket 监听端口设为 3001&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reportSelfMessage: true&lt;/code&gt; —— 后面处理 bot 自发消息要用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;注意：NapCat 和桌面版 QQ 不能同时登录同一个号。&lt;/strong&gt; 测试时得用手机 QQ 或另一个号。&lt;/p&gt;
&lt;h2&gt;NoneBot2 项目搭建&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python -m venv .venv
source .venv/bin/activate
pip install nonebot2[fastapi,websockets] nonebot-adapter-onebot httpx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;bot.py&lt;/code&gt; 就几行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import nonebot
from nonebot.adapters.onebot.v11 import Adapter

nonebot.init()
driver = nonebot.get_driver()
driver.register_adapter(Adapter)
nonebot.load_plugins(&quot;plugins&quot;)

if __name__ == &quot;__main__&quot;:
    nonebot.run()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;.env&lt;/code&gt; 配置：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-env&quot;&gt;DRIVER=~fastapi+~websockets
HOST=127.0.0.1
PORT=8080
COMMAND_START=[&quot;#&quot;]
ONEBOT_WS_URLS=[&quot;ws://127.0.0.1:3001&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;搜索逻辑&lt;/h2&gt;
&lt;h3&gt;网易云音乐 API&lt;/h3&gt;
&lt;p&gt;用的是网易云非官方的搜索接口：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;async with httpx.AsyncClient(timeout=10) as client:
    response = await client.post(
        &quot;https://music.163.com/api/search/get&quot;,
        data={&quot;s&quot;: keyword, &quot;type&quot;: 1, &quot;limit&quot;: 20, &quot;offset&quot;: 0},
        headers={&quot;Referer&quot;: &quot;https://music.163.com&quot;},
    )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Referer&lt;/code&gt; 头必须带，不然返回 403。&lt;/p&gt;
&lt;h3&gt;模糊匹配&lt;/h3&gt;
&lt;p&gt;搜索结果往往不是精确匹配，比如搜&quot;我爱你&quot;，返回的可能是&quot;我爱你中国&quot;、&quot;我爱你不是因为你美丽&quot;之类的。用 &lt;code&gt;difflib.SequenceMatcher&lt;/code&gt; 做模糊匹配：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from difflib import SequenceMatcher

scored = [
    (song, SequenceMatcher(None, keyword, song[&quot;name&quot;]).ratio())
    for song in songs
]
scored.sort(key=lambda x: x[1], reverse=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;阈值设 0.4——低于这个分数认为没有匹配，但还是会把最接近的结果作为 fallback 返回。&lt;/p&gt;
&lt;h3&gt;QQ 音乐 fallback&lt;/h3&gt;
&lt;p&gt;网易云搜不到的话，再试 QQ 音乐：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;async with httpx.AsyncClient(timeout=10) as client:
    response = await client.get(
        &quot;https://c.y.qq.com/soso/fcgi-bin/client_search_cp&quot;,
        params={&quot;w&quot;: keyword, &quot;p&quot;: 1, &quot;n&quot;: 20, &quot;format&quot;: &quot;json&quot;, &quot;t&quot;: 0},
        headers={&quot;Referer&quot;: &quot;https://y.qq.com&quot;},
    )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;两个源都搜完后，取分数最高的那个结果。&lt;/p&gt;
&lt;h2&gt;踩坑记录&lt;/h2&gt;
&lt;h3&gt;坑 1：音乐卡片格式&lt;/h3&gt;
&lt;p&gt;OneBot v11 的音乐卡片有两种格式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;简写格式&lt;/strong&gt;：&lt;code&gt;type: &quot;163&quot;&lt;/code&gt; + &lt;code&gt;id&lt;/code&gt;，让协议端自己去拉歌曲信息&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自定义格式&lt;/strong&gt;：&lt;code&gt;type: &quot;custom&quot;&lt;/code&gt; + 完整的 url/audio/title/content/image&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一开始用了简写格式，结果 NapCat 直接报错：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ActionFailed: 消息体无法解析, 请检查是否发送了不支持的消息类型
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;NapCat 不支持简写格式的音乐卡片解析。改成 custom 格式，手动提供所有字段就好了：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;MessageSegment(
    type=&quot;music&quot;,
    data={
        &quot;type&quot;: &quot;custom&quot;,
        &quot;url&quot;: f&quot;https://music.163.com/song?id={song_id}&quot;,
        &quot;audio&quot;: f&quot;https://music.163.com/song/media/outer/url?id={song_id}.mp3&quot;,
        &quot;title&quot;: song_name,
        &quot;content&quot;: artist_name,
        &quot;image&quot;: pic_url,
    },
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;坑 2：bot 自发消息不被识别&lt;/h3&gt;
&lt;p&gt;需求是 bot 自己在聊天窗口输入 &lt;code&gt;#歌名&lt;/code&gt; 也能触发。NapCat 上报自发消息的 &lt;code&gt;post_type&lt;/code&gt; 是 &lt;code&gt;message_sent&lt;/code&gt;，但 NoneBot2 只认 &lt;code&gt;message&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;解决方案是自定义 Event 类，覆盖 &lt;code&gt;get_type()&lt;/code&gt; 返回 &lt;code&gt;&quot;message&quot;&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class SelfSentMessageEvent(MessageEvent):
    post_type: Literal[&quot;message_sent&quot;]

    def get_type(self) -&gt; str:
        return &quot;message&quot;

class SelfSentPrivateMessageEvent(SelfSentMessageEvent):
    message_type: Literal[&quot;private&quot;]
    target_id: int = 0

Adapter.add_custom_model(SelfSentPrivateMessageEvent)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Adapter.add_custom_model()&lt;/code&gt; 的参数必须是 Event 子类，不能传字符串——它通过 Literal 类型注解自动推断匹配规则。&lt;/p&gt;
&lt;h3&gt;坑 3：自发消息发回给了自己&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;bot.send(event, message)&lt;/code&gt; 对自发消息会发回给 bot 自己，因为 &lt;code&gt;user_id == self_id&lt;/code&gt;。得判断事件类型，用 &lt;code&gt;target_id&lt;/code&gt; 发给实际的聊天对方：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;async def _send(bot, event, message):
    if isinstance(event, SelfSentPrivateMessageEvent) and event.target_id:
        await bot.send_private_msg(user_id=event.target_id, message=message)
    else:
        await bot.send(event, message)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;坑 4：网易云 album.picUrl 经常为空&lt;/h3&gt;
&lt;p&gt;搜索结果里 &lt;code&gt;album.picUrl&lt;/code&gt; 不一定有值，但 &lt;code&gt;album.picId&lt;/code&gt; 通常有。可以用 picId 拼封面 URL：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;pic_url = album.get(&quot;picUrl&quot;) or &quot;&quot;
if not pic_url:
    pic_id = album.get(&quot;picId&quot;)
    if pic_id:
        pic_url = f&quot;https://p1.music.126.net/{pic_id}/{pic_id}.jpg&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过用了 custom 卡片格式后，封面字段对 QQ 客户端的渲染影响不大，空着也能正常显示。&lt;/p&gt;
&lt;h2&gt;仅响应私聊&lt;/h2&gt;
&lt;p&gt;handler 里加个 isinstance 检查就行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;@song_cmd.handle()
async def handle_song(bot: Bot, event: MessageEvent):
    if not isinstance(event, (PrivateMessageEvent, SelfSentPrivateMessageEvent)):
        return
    # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;最终效果&lt;/h2&gt;
&lt;p&gt;私聊 bot 发 &lt;code&gt;#我爱你&lt;/code&gt;，收到一张可以直接播放的音乐卡片。bot 自己在聊天窗口发也能触发。&lt;/p&gt;
&lt;p&gt;项目代码：&lt;a href=&quot;https://github.com/stoicneko/nonebot-diange&quot;&gt;GitHub - nonebot-diange&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;整个项目的核心逻辑不复杂——搜索 API + 模糊匹配 + 构造卡片消息。大部分时间花在了 NapCat 的部署和各种边界情况（自发消息、卡片格式、封面 URL）上。&lt;/p&gt;
&lt;p&gt;几个经验：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NapCat 的 custom 音乐卡片比简写格式可靠得多&lt;/li&gt;
&lt;li&gt;NoneBot2 的自定义 Event 机制很灵活，但文档不太够，得看源码&lt;/li&gt;
&lt;li&gt;非官方 API 随时可能变，做好降级方案（双源 fallback）比较稳妥&lt;/li&gt;
&lt;li&gt;第三方 QQ 协议端有封号风险，建议用小号&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>用 Tailscale 从其他设备 SSH 进 WSL2</title><link>https://blogs-6hn.pages.dev/blog/wsl2-tailscale-ssh</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/wsl2-tailscale-ssh</guid><description>校园网/企业网 AP 隔离导致设备间无法直连，用 Tailscale 建立 P2P 隧道，五分钟解决跨设备 SSH 连接 WSL2 的问题。</description><pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;用 Tailscale 从其他设备 SSH 进 WSL2&lt;/h1&gt;
&lt;p&gt;想从手机或另一台电脑 SSH 进自己的 WSL2，折腾了一圈，最后发现根本不是 SSH 配置的问题。&lt;/p&gt;
&lt;h2&gt;问题排查&lt;/h2&gt;
&lt;p&gt;WSL2 用的是 Mirrored 网络模式，和 Windows 共享同一个 IP（比如 &lt;code&gt;10.66.180.15&lt;/code&gt;）。SSH 已经跑在端口 3456，Windows 防火墙也开了，但另一台设备死活连不上。&lt;/p&gt;
&lt;p&gt;加 &lt;code&gt;-v&lt;/code&gt; 参数也没走到握手阶段，直接 timeout。于是在另一台设备上 ping：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ping 10.66.180.15
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一直 timeout。再查 ARP 表：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;arp -n
10.66.180.15    (incomplete)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;ARP incomplete&lt;/strong&gt;——说明网络层根本没有收到对方的响应，IP 在网络上&quot;不存在&quot;。&lt;/p&gt;
&lt;p&gt;这不是 SSH 的锅，也不是 WSL 配置的锅。&lt;/p&gt;
&lt;h2&gt;真正的原因：AP 客户端隔离&lt;/h2&gt;
&lt;p&gt;学校和公司的 Wi-Fi 普遍开启 &lt;strong&gt;Client Isolation&lt;/strong&gt;（客户端隔离），同一个无线网络下的设备之间无法直接通信。路由器可以 ping 通，但设备互相 ping 不通。&lt;/p&gt;
&lt;p&gt;端口转发、防火墙规则、WSL 网络配置——全是白费力气。&lt;/p&gt;
&lt;h2&gt;解决方案：Tailscale&lt;/h2&gt;
&lt;p&gt;Tailscale 通过 DERP 中继或直接 P2P 打洞，绕过 AP 隔离，建立加密隧道。&lt;/p&gt;
&lt;h3&gt;1. WSL2 中安装&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://tailscale.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 启动并登录&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo tailscaled &amp;#x26;
sudo tailscale up
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;终端会输出一个授权链接，浏览器打开，用 GitHub/Google 账号登录即可。&lt;/p&gt;
&lt;h3&gt;3. 获取 Tailscale IP&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;tailscale ip
# 100.x.x.x
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 另一台设备&lt;/h3&gt;
&lt;p&gt;去 &lt;a href=&quot;https://tailscale.com/download&quot;&gt;tailscale.com/download&lt;/a&gt; 下载对应平台的客户端，登录同一个账号。&lt;/p&gt;
&lt;p&gt;然后就可以连了：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -p 3456 zhaole_lv@100.x.x.x
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;遇到连接问题先在网络层排查，&lt;code&gt;ping&lt;/code&gt; + &lt;code&gt;arp&lt;/code&gt; 两条命令就能判断是不是网络隔离。如果 ARP incomplete，再怎么配 SSH 都没用，Tailscale 是最省事的解法。&lt;/p&gt;</content:encoded></item><item><title>在 WSL 中提取 Chrome Cookie：从 PyPI 撞名到 App-Bound Encryption</title><link>https://blogs-6hn.pages.dev/blog/wsl-chrome-cookie-extraction</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/wsl-chrome-cookie-extraction</guid><description>想用 boss-cli 在终端刷 BOSS直聘职位，结果踩了一连串坑：PyPI 包撞名、Chrome v127 App-Bound Encryption 加密、反 DevTools 检测、session 不匹配……记录完整排查过程。</description><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;在 WSL 中提取 Chrome Cookie：从 PyPI 撞名到 App-Bound Encryption&lt;/h1&gt;
&lt;p&gt;想在终端里直接刷 BOSS直聘职位，发现有个开源工具 &lt;a href=&quot;https://github.com/jackwener/boss-cli&quot;&gt;kabi-boss-cli&lt;/a&gt;，于是开始折腾。没想到一个&quot;装个包、登录、搜索&quot;的简单任务，踩了好几个坑。&lt;/p&gt;
&lt;h2&gt;第一坑：PyPI 包撞名&lt;/h2&gt;
&lt;p&gt;项目 README 写的安装命令是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;uv tool install boss-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;装完之后 &lt;code&gt;boss --help&lt;/code&gt; 只有一个 &lt;code&gt;init&lt;/code&gt; 命令，没有 &lt;code&gt;login&lt;/code&gt;、&lt;code&gt;search&lt;/code&gt; 之类的。&lt;/p&gt;
&lt;p&gt;原因是 PyPI 上恰好有个完全不相关的项目也叫 &lt;code&gt;boss-cli&lt;/code&gt;（一个服务器部署工具），而 &lt;code&gt;jackwener/boss-cli&lt;/code&gt; 根本没有发布到 PyPI。&lt;/p&gt;
&lt;p&gt;后来作者把包名改成了 &lt;code&gt;kabi-boss-cli&lt;/code&gt; 并发布到 PyPI，正确的安装方式：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;uv tool install kabi-boss-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第二坑：QR 登录拿不到 &lt;code&gt;__zp_stoken__&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;安装完之后用 &lt;code&gt;boss login&lt;/code&gt; 扫码登录，&lt;code&gt;boss status&lt;/code&gt; 显示已登录（4 个 cookies），但一执行 &lt;code&gt;boss search&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;ok&quot;: false,
  &quot;error&quot;: {
    &quot;code&quot;: &quot;not_authenticated&quot;,
    &quot;message&quot;: &quot;环境异常 (__zp_stoken__ 已过期)。请重新登录: boss logout &amp;#x26;&amp;#x26; boss login&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;反复 &lt;code&gt;logout &amp;#x26;&amp;#x26; login&lt;/code&gt; 没用。查看 credential 文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;cookies&quot;: {
    &quot;wt2&quot;: &quot;...&quot;,
    &quot;wbg&quot;: &quot;0&quot;,
    &quot;zp_at&quot;: &quot;...&quot;,
    &quot;bst&quot;: &quot;...&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;少了 &lt;code&gt;__zp_stoken__&lt;/code&gt;。这个 cookie 是 BOSS直聘网页端 JavaScript 动态生成的，APP 扫码登录流程根本不会产生它，只有在浏览器里访问 &lt;code&gt;zhipin.com&lt;/code&gt; 才会被设置。&lt;/p&gt;
&lt;h2&gt;第三坑：在 WSL 里提取 Chrome Cookie&lt;/h2&gt;
&lt;p&gt;知道问题所在了：需要从 Windows Chrome 里拿到完整的 cookie，写进 credential 文件。&lt;/p&gt;
&lt;h3&gt;尝试一：browser-cookie3&lt;/h3&gt;
&lt;p&gt;boss-cli 本身就依赖 &lt;code&gt;browser-cookie3&lt;/code&gt; 库，所以有 &lt;code&gt;boss login --cookie-source chrome&lt;/code&gt; 命令。但在 WSL 下，Chrome 是 Windows 应用，cookie 文件在 Windows 路径下，WSL Python 找不到。&lt;/p&gt;
&lt;p&gt;换 Windows Python（&lt;code&gt;D:\Python3\python.exe&lt;/code&gt;）试试：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;/mnt/d/Python3/python.exe -m pip install browser-cookie3
/mnt/d/Python3/python.exe -c &quot;import browser_cookie3 as bc3; print(bc3.chrome(domain_name=&apos;.zhipin.com&apos;))&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;报错：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;browser_cookie3.BrowserCookieError: Unable to get key for cookie decryption
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原因是 Chrome v127 引入了 &lt;strong&gt;App-Bound Encryption&lt;/strong&gt;，cookie 加密不再只依赖 DPAPI，而是绑定到 Chrome 进程本身，第三方程序（包括 browser-cookie3）无法解密。&lt;/p&gt;
&lt;h3&gt;尝试二：直接读 SQLite 文件&lt;/h3&gt;
&lt;p&gt;Chrome cookie 存在 SQLite 数据库里：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;C:\Users\&amp;#x3C;用户名&gt;\AppData\Local\Google\Chrome\User Data\Default\Network\Cookies
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Chrome 运行时文件被锁，关掉 Chrome 用 PowerShell 复制后，用 sqlite3 查询：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sqlite3 /mnt/c/Temp/chrome_cookies.db \
  &quot;SELECT name, is_httponly, expires_utc FROM cookies WHERE host_key LIKE &apos;%zhipin%&apos;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到 cookie 名字和元信息（包括 &lt;code&gt;__zp_stoken__&lt;/code&gt; 存在、未过期、非 HttpOnly），但值是加密的，依旧无法解密。&lt;/p&gt;
&lt;h3&gt;尝试三：Bookmarklet（部分成功）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;__zp_stoken__&lt;/code&gt; 不是 HttpOnly，JavaScript 可以读取。BOSS直聘会检测 F12 开发者工具导致页面崩溃，但书签里的 JavaScript 不受限制：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;javascript:void(prompt(&quot;cookies&quot;,JSON.stringify(Object.fromEntries(document.cookie.split(&quot;;&quot;).map(c=&gt;[c.trim().split(&quot;=&quot;)[0],c.trim().split(&quot;=&quot;).slice(1).join(&quot;=&quot;)])))))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;拿到了 &lt;code&gt;__zp_stoken__&lt;/code&gt; 等非 HttpOnly cookie。但写入 credential 文件后仍然报错。&lt;/p&gt;
&lt;h3&gt;关键发现：Session 不匹配&lt;/h3&gt;
&lt;p&gt;排查发现，&lt;strong&gt;QR 登录的 &lt;code&gt;wt2&lt;/code&gt;/&lt;code&gt;zp_at&lt;/code&gt; 和浏览器的 &lt;code&gt;__zp_stoken__&lt;/code&gt; 属于不同 session&lt;/strong&gt;，服务器会验证它们是否匹配。混用两套 session 的 cookie 会被拒绝。&lt;/p&gt;
&lt;p&gt;用 curl 直接测试也确认了这一点：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -s &quot;https://www.zhipin.com/wapi/zpgeek/search/joblist.json?query=Python&quot; \
  --cookie &quot;wt2=&amp;#x3C;QR登录的值&gt;; __zp_stoken__=&amp;#x3C;浏览器的值&gt;&quot;
# 返回 code=37, &quot;您的环境存在异常&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以必须&lt;strong&gt;从同一个浏览器 session 中同时获取所有 cookie&lt;/strong&gt;，包括 HttpOnly 的 &lt;code&gt;wt2&lt;/code&gt; 和 &lt;code&gt;zp_at&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;尝试四：Playwright（失败）&lt;/h3&gt;
&lt;p&gt;试图用 Playwright 挂载 Chrome 现有 profile 提取 cookie：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用默认 profile → Chrome 报错 &quot;DevTools remote debugging requires a non-default data directory&quot;&lt;/li&gt;
&lt;li&gt;使用临时 profile → 触发 BOSS直聘反自动化检测&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;尝试五：Chrome 远程调试端口（失败）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;powershell.exe -Command &quot;Start-Process chrome.exe -ArgumentList &apos;--remote-debugging-port=9222&apos;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;WSL2 的 &lt;code&gt;localhost&lt;/code&gt; 不通 Windows，用 Windows IP 也无响应——Chrome 在已有用户数据目录的情况下会复用现有实例，忽略调试端口参数。&lt;/p&gt;
&lt;h3&gt;最终方案：EditThisCookie 扩展&lt;/h3&gt;
&lt;p&gt;Chrome 内置的 Cookie-Editor 扩展只导出了非 HttpOnly cookie，&lt;strong&gt;缺少关键的 &lt;code&gt;wt2&lt;/code&gt;、&lt;code&gt;zp_at&lt;/code&gt;&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;换用 &lt;strong&gt;EditThisCookie&lt;/strong&gt; 扩展后，成功导出了所有 cookie（包括 HttpOnly），拿到了来自同一 session 的完整 cookie 集合：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;wt2&quot;: &quot;...(HttpOnly)&quot;,
  &quot;zp_at&quot;: &quot;...(HttpOnly)&quot;,
  &quot;wbg&quot;: &quot;0(HttpOnly)&quot;,
  &quot;bst&quot;: &quot;...&quot;,
  &quot;__zp_stoken__&quot;: &quot;...&quot;,
  &quot;__a&quot;: &quot;...&quot;,
  &quot;__c&quot;: &quot;...&quot;,
  ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将所有 cookie 写入 &lt;code&gt;~/.config/boss-cli/credential.json&lt;/code&gt;，&lt;strong&gt;&lt;code&gt;boss search&lt;/code&gt; 终于成功返回职位列表&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;| 坑 | 原因 | 解法 |
|---|---|---|
| 安装了错误的包 | PyPI 有同名不同包 | &lt;code&gt;uv tool install kabi-boss-cli&lt;/code&gt; |
| &lt;code&gt;__zp_stoken__&lt;/code&gt; 缺失 | QR 登录不经过网页端 JS | 从浏览器提取 |
| browser-cookie3 失败 | Chrome v127 App-Bound Encryption | 放弃，改用扩展 |
| Bookmarklet 拿到 cookie 但不能用 | &lt;code&gt;wt2&lt;/code&gt;/&lt;code&gt;zp_at&lt;/code&gt;（HttpOnly）和 &lt;code&gt;__zp_stoken__&lt;/code&gt; 来自不同 session | 必须从同一 session 提取全部 cookie |
| Cookie-Editor 扩展不完整 | 未导出 HttpOnly cookie | 换用 EditThisCookie |
| Playwright / CDP 失败 | 反自动化检测 + WSL2 网络隔离 | 放弃 |&lt;/p&gt;
&lt;h3&gt;正确流程（WSL + Chrome）&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;在 Chrome 中登录 &lt;code&gt;zhipin.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;安装 &lt;strong&gt;EditThisCookie&lt;/strong&gt; 扩展&lt;/li&gt;
&lt;li&gt;在 zhipin.com 页面点击 EditThisCookie 图标，导出所有 cookie&lt;/li&gt;
&lt;li&gt;URL 解码 &lt;code&gt;__zp_stoken__&lt;/code&gt; 等含 &lt;code&gt;%xx&lt;/code&gt; 的值&lt;/li&gt;
&lt;li&gt;写入 &lt;code&gt;~/.config/boss-cli/credential.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;boss search&lt;/code&gt; 正常工作&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：&lt;code&gt;__zp_stoken__&lt;/code&gt; 有过期时间（通常几天），过期后需要重新用 EditThisCookie 提取一次。&lt;/p&gt;</content:encoded></item><item><title>使用 Astro + Cloudflare Pages 搭建个人博客</title><link>https://blogs-6hn.pages.dev/blog/building-blog-with-astro-and-cloudflare</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/building-blog-with-astro-and-cloudflare</guid><description>记录从零开始使用 astro-theme-pure 模板搭建个人博客并部署到 Cloudflare Pages 的完整过程，包括踩过的坑和解决方案。</description><pubDate>Thu, 05 Mar 2026 12:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;为什么选择 Astro + Cloudflare Pages&lt;/h2&gt;
&lt;p&gt;选择 &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; 是因为它专注于内容驱动的网站，构建速度快，默认零 JS 加载。主题选用了 &lt;a href=&quot;https://github.com/cworld1/astro-theme-pure&quot;&gt;astro-theme-pure&lt;/a&gt;，简洁美观，功能齐全。&lt;/p&gt;
&lt;p&gt;部署平台选了 Cloudflare Pages，免费额度够用，全球 CDN 速度快，和 GitHub 集成后 push 即部署，非常方便。&lt;/p&gt;
&lt;h2&gt;搭建过程&lt;/h2&gt;
&lt;h3&gt;1. 克隆模板&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/cworld1/astro-theme-pure.git blogs
cd blogs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;模板本身是一个 monorepo 结构，主题核心在 &lt;code&gt;packages/pure/&lt;/code&gt; 下，站点配置在 &lt;code&gt;src/site.config.ts&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;2. 切换适配器&lt;/h3&gt;
&lt;p&gt;模板默认使用 Vercel 适配器，需要改为 Cloudflare：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install @astrojs/cloudflare
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改 &lt;code&gt;astro.config.ts&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import cloudflare from &apos;@astrojs/cloudflare&apos;

export default defineConfig({
  adapter: cloudflare({ imageService: &apos;compile&apos; }),
  output: &apos;server&apos;,
  // ...
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里有个注意点：Cloudflare 适配器使用 &lt;code&gt;compile&lt;/code&gt; 模式处理图片，不需要 &lt;code&gt;sharp&lt;/code&gt;。如果配置里还保留了 &lt;code&gt;image.service: sharp&lt;/code&gt;，需要删掉，否则会冲突。&lt;/p&gt;
&lt;h3&gt;3. 添加 Wrangler 配置&lt;/h3&gt;
&lt;p&gt;创建 &lt;code&gt;wrangler.jsonc&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;name&quot;: &quot;blogs&quot;,
  &quot;pages_build_output_dir&quot;: &quot;dist&quot;,
  &quot;compatibility_date&quot;: &quot;2026-03-05&quot;,
  &quot;compatibility_flags&quot;: [&quot;nodejs_compat&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键字段是 &lt;code&gt;pages_build_output_dir&lt;/code&gt;，告诉 Cloudflare 构建产物在 &lt;code&gt;dist&lt;/code&gt; 目录。&lt;code&gt;nodejs_compat&lt;/code&gt; 是必须的兼容性标志，否则一些 Node.js API 在 Cloudflare Workers 运行时中不可用。&lt;/p&gt;
&lt;h3&gt;4. 依赖问题&lt;/h3&gt;
&lt;p&gt;模板原本使用 bun 管理依赖，切换到 npm 后可能遇到依赖缺失的问题。我碰到了 &lt;code&gt;@unocss/astro&lt;/code&gt; 找不到的情况，手动安装解决：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install @unocss/astro
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 自定义站点信息&lt;/h3&gt;
&lt;p&gt;编辑 &lt;code&gt;src/site.config.ts&lt;/code&gt;，修改标题、作者、描述、社交链接等。同时更新 &lt;code&gt;astro.config.ts&lt;/code&gt; 中的 &lt;code&gt;site&lt;/code&gt; 字段为实际域名。&lt;/p&gt;
&lt;h2&gt;踩坑记录&lt;/h2&gt;
&lt;h3&gt;Worker vs Pages：最大的坑&lt;/h3&gt;
&lt;p&gt;这是整个过程中最折腾的一个问题。在 Cloudflare Dashboard 创建项目时，我选成了 &lt;strong&gt;Worker&lt;/strong&gt; 项目而不是 &lt;strong&gt;Pages&lt;/strong&gt; 项目。&lt;/p&gt;
&lt;p&gt;这两者的区别：&lt;/p&gt;
&lt;p&gt;| | Workers | Pages |
|---|---|---|
| 用途 | API、边缘计算 | 静态站点 + SSR |
| 配置 | &lt;code&gt;main&lt;/code&gt; + &lt;code&gt;assets&lt;/code&gt; | &lt;code&gt;pages_build_output_dir&lt;/code&gt; |
| 部署命令 | &lt;code&gt;wrangler deploy&lt;/code&gt; | &lt;code&gt;wrangler pages deploy&lt;/code&gt; |&lt;/p&gt;
&lt;p&gt;因为选错了项目类型，构建成功后部署一直失败，报错 &lt;code&gt;Must specify a project name&lt;/code&gt;。反复修改配置都没用，最后发现根本原因就是项目类型不对。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案&lt;/strong&gt;：删掉 Worker 项目，重新创建 Pages 项目，连接 GitHub 仓库，构建命令填 &lt;code&gt;npm run build&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;Node 版本问题&lt;/h3&gt;
&lt;p&gt;Cloudflare 构建环境默认使用 Node 22，项目依赖也要求 Node &gt;= 20。如果通过 &lt;code&gt;.node-version&lt;/code&gt; 文件指定了 Node 18，会导致 &lt;code&gt;oxc-parser&lt;/code&gt; 等包找不到原生绑定而构建失败。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;教训&lt;/strong&gt;：不要随意降低 Node 版本，先看看依赖的最低要求。&lt;/p&gt;
&lt;h3&gt;image service 冲突&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;astro.config.ts&lt;/code&gt; 中同时配置了 &lt;code&gt;sharp&lt;/code&gt; image service 和 Cloudflare 适配器的 &lt;code&gt;compile&lt;/code&gt; 模式，需要去掉 &lt;code&gt;sharp&lt;/code&gt; 的配置：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// 删掉这部分
image: {
  service: {
    entrypoint: &apos;astro/assets/services/sharp&apos;
  }
}

// 只保留
image: {
  responsiveStyles: true
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;最终项目结构&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;blogs/
├── astro.config.ts          # Astro 配置
├── wrangler.jsonc            # Cloudflare Pages 配置
├── src/
│   ├── site.config.ts        # 站点信息配置
│   ├── content/blog/         # 博客文章
│   └── assets/               # 静态资源
├── packages/pure/            # 主题核心包
└── public/                   # 公共静态文件
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;整个搭建过程其实并不复杂，核心步骤就是：克隆模板 → 换适配器 → 加 wrangler 配置 → Cloudflare Pages 连接 GitHub。&lt;/p&gt;
&lt;p&gt;最大的坑是在 Cloudflare 上创建了错误的项目类型（Worker 而非 Pages），导致走了很多弯路。记住：&lt;strong&gt;部署静态站点或 SSR 网站用 Pages，部署 API 或边缘函数用 Workers&lt;/strong&gt;。&lt;/p&gt;</content:encoded></item><item><title>在 iPhone 上用 Agmente 远程连接 Claude Code</title><link>https://blogs-6hn.pages.dev/blog/%E5%9C%A8-iphone-%E4%B8%8A%E7%94%A8-agmente-%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5-claude-code</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/%E5%9C%A8-iphone-%E4%B8%8A%E7%94%A8-agmente-%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5-claude-code</guid><description>Agmente 是一个 iOS 客户端，可以通过 ACP（Agent Client Protocol）连接到远程编码代理。本文记录如何在 Linux 服务器（WSL2）上配置 Claude Code，让 iPhone 上的 Agmente 随时随地与 Claude Code 对话。</description><pubDate>Thu, 05 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;在 iPhone 上用 Agmente 远程连接 Claude Code&lt;/h1&gt;
&lt;p&gt;Agmente 是一个 iOS 客户端，可以通过 ACP（Agent Client Protocol）连接到远程编码代理。本文记录如何在 Linux 服务器（WSL2）上配置 Claude Code，让 iPhone 上的 Agmente 随时随地与 Claude Code 对话。&lt;/p&gt;
&lt;h2&gt;架构概览&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;iPhone (Agmente App)
    |
    | wss:// (加密 WebSocket)
    |
Cloudflare Tunnel (快速隧道或命名隧道)
    |
    | http://localhost:8765
    |
stdio-to-ws (WebSocket 桥接)
    |
    | stdin/stdout (ACP 协议)
    |
Claude Code ACP 适配器
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agmente 通过 WebSocket 与服务器通信。&lt;code&gt;stdio-to-ws&lt;/code&gt; 负责将 Claude Code 的 ACP stdio 协议桥接为 WebSocket。Cloudflare Tunnel 提供免费的公网 HTTPS 入口，不需要你拥有公网 IP。本文提供两种隧道方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;方案 A：快速隧道&lt;/strong&gt; — 零配置，无需域名，但每次启动 URL 会变&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;方案 B：命名隧道&lt;/strong&gt; — 需要一个域名，但 URL 固定，一次配置永久使用&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;前置条件&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一台 Linux 机器（本文以 Arch Linux / WSL2 为例）&lt;/li&gt;
&lt;li&gt;Node.js 18+&lt;/li&gt;
&lt;li&gt;Claude Code CLI 已安装并登录（&lt;code&gt;claude&lt;/code&gt; 命令可用）&lt;/li&gt;
&lt;li&gt;iPhone 上安装 &lt;a href=&quot;https://apps.apple.com/us/app/agmente/id6756249477&quot;&gt;Agmente App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;（方案 B 额外需要）一个域名（任意注册商均可，需将 DNS 托管到 Cloudflare）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;第一步：安装 cloudflared&lt;/h2&gt;
&lt;p&gt;Cloudflare Tunnel 客户端，用于将本地端口暴露到公网。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Arch Linux
paru -S cloudflared

# Debian/Ubuntu
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloudflare-main.gpg
echo &apos;deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main&apos; | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update &amp;#x26;&amp;#x26; sudo apt install cloudflared

# macOS
brew install cloudflared
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第二步：启动 Claude Code ACP 桥接&lt;/h2&gt;
&lt;p&gt;使用 &lt;code&gt;@rebornix/stdio-to-ws&lt;/code&gt; 将 Claude Code ACP 适配器桥接到 WebSocket：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;env -u CLAUDECODE npx -y @rebornix/stdio-to-ws \
  --persist \
  --grace-period 604800 \
  &quot;npx @zed-industries/claude-code-acp&quot; \
  --port 8765
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参数说明：&lt;/p&gt;
&lt;p&gt;| 参数                    | 作用                                         |
| ----------------------- | -------------------------------------------- |
| &lt;code&gt;env -u CLAUDECODE&lt;/code&gt;     | 清除环境变量，避免嵌套会话检测冲突           |
| &lt;code&gt;--persist&lt;/code&gt;             | 保持 Claude Code 进程在 WebSocket 断连后存活 |
| &lt;code&gt;--grace-period 604800&lt;/code&gt; | 允许 7 天内重新连接而不丢失会话              |
| &lt;code&gt;--port 8765&lt;/code&gt;           | WebSocket 监听端口                           |&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;踩坑提醒&lt;/strong&gt;：如果你在 Claude Code 终端内启动这个命令，子进程会继承 &lt;code&gt;CLAUDECODE&lt;/code&gt; 环境变量，导致报错 &quot;Claude Code cannot be launched inside another Claude Code session&quot;。务必加上 &lt;code&gt;env -u CLAUDECODE&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;启动成功后会看到：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[stdio-to-ws] WebSocket server listening on port 8765 (persistence enabled, grace period: 604800s)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第三步：启动 Cloudflare 隧道&lt;/h2&gt;
&lt;h3&gt;方案 A：快速隧道（无需域名，URL 每次变化）&lt;/h3&gt;
&lt;p&gt;最简单的方式，零配置，适合临时使用或试用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cloudflared tunnel --url http://localhost:8765
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出中会包含一个临时公网 URL：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://xxx-xxx-xxx-xxx.trycloudflare.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;记下这个 URL，后面要用。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;快速隧道的 URL 每次启动都会变，需要重新在 Agmente 中添加 server。如果你希望 URL 固定，请使用方案 B。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;方案 B：命名隧道（需要域名，URL 固定）&lt;/h3&gt;
&lt;p&gt;使用命名隧道可以获得固定域名，一次配置永久使用，无需每次重新添加 server。&lt;/p&gt;
&lt;h4&gt;3B.1 将域名托管到 Cloudflare&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;登录 &lt;a href=&quot;https://dash.cloudflare.com&quot;&gt;Cloudflare Dashboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;点击 &quot;Add a site&quot;，输入你的域名&lt;/li&gt;
&lt;li&gt;选择 Free 计划&lt;/li&gt;
&lt;li&gt;Cloudflare 会给你两个 NS 服务器地址&lt;/li&gt;
&lt;li&gt;去域名注册商（如阿里云）将 DNS 服务器改为 Cloudflare 提供的地址&lt;/li&gt;
&lt;li&gt;等待域名状态变为 Active&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;3B.2 登录 cloudflared 并创建隧道&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 登录（浏览器中选择你的域名授权）
cloudflared tunnel login

# 创建命名隧道
cloudflared tunnel create agmente-claude

# 配置 DNS 路由（将子域名指向隧道）
cloudflared tunnel route dns agmente-claude claude.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3B.3 编写隧道配置文件&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# ~/.cloudflared/config.yml
tunnel: agmente-claude
credentials-file: ~/.cloudflared/&amp;#x3C;TUNNEL_ID&gt;.json

ingress:
  - hostname: claude.yourdomain.com
    service: http://localhost:8765
  - service: http_status:404
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;&amp;#x3C;TUNNEL_ID&gt;&lt;/code&gt; 替换为 &lt;code&gt;cloudflared tunnel create&lt;/code&gt; 输出的隧道 ID。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;3B.4 启动隧道&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cloudflared tunnel --config ~/.cloudflared/config.yml run
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在 &lt;code&gt;wss://claude.yourdomain.com&lt;/code&gt; 就是你的固定地址，每次启动都不会变。&lt;/p&gt;
&lt;h2&gt;第四步：在 Agmente 中连接&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;打开 iPhone 上的 Agmente App&lt;/li&gt;
&lt;li&gt;点击添加新 Agent&lt;/li&gt;
&lt;li&gt;URL 填入：
&lt;ul&gt;
&lt;li&gt;方案 A：&lt;code&gt;wss://xxx-xxx-xxx-xxx.trycloudflare.com&lt;/code&gt;（替换为你的实际 URL）&lt;/li&gt;
&lt;li&gt;方案 B：&lt;code&gt;wss://claude.yourdomain.com&lt;/code&gt;（替换为你的实际域名）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;协议默认为 ACP，无需修改&lt;/li&gt;
&lt;li&gt;点击连接&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;连接成功后，绿灯亮起，创建新 Session 即可开始对话。&lt;/p&gt;
&lt;h2&gt;一键启动脚本&lt;/h2&gt;
&lt;h3&gt;方案 A 脚本（快速隧道）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/bash
# ~/bin/agmente-start.sh

# 启动 ACP 桥接
env -u CLAUDECODE npx -y @rebornix/stdio-to-ws \
  --persist --grace-period 604800 \
  &quot;npx @zed-industries/claude-code-acp&quot; \
  --port 8765 &amp;#x26;
WS_PID=$!

# 等待 WebSocket 就绪
sleep 3

# 启动 Cloudflare 隧道
cloudflared tunnel --url http://localhost:8765 &amp;#x26;
CF_PID=$!

echo &quot;服务已启动 (stdio-to-ws PID: $WS_PID, cloudflared PID: $CF_PID)&quot;
echo &quot;查看 cloudflared 输出获取公网 URL&quot;
echo &quot;按 Ctrl+C 停止所有服务&quot;

trap &quot;kill $WS_PID $CF_PID 2&gt;/dev/null; exit&quot; INT TERM
wait
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;方案 B 脚本（命名隧道，固定 URL）&lt;/h3&gt;
&lt;p&gt;使用命名隧道后，每次启动的 URL 都是固定的，还支持同时启动多个代理：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/bash
# ~/bin/agmente-start.sh
# 用法: agmente-start.sh [claude|codex|both]

MODE=${1:-both}

cleanup() {
    echo &quot;&quot;
    echo &quot;正在停止所有服务...&quot;
    kill $WS_PID $CODEX_PID $CF1_PID $CF2_PID 2&gt;/dev/null
    exit 0
}
trap cleanup INT TERM

if [[ &quot;$MODE&quot; == &quot;claude&quot; || &quot;$MODE&quot; == &quot;both&quot; ]]; then
    echo &quot;启动 Claude Code ACP 桥接 (端口 8765)...&quot;
    env -u CLAUDECODE npx -y @rebornix/stdio-to-ws \
        --persist --grace-period 604800 \
        &quot;npx @zed-industries/claude-code-acp&quot; --port 8765 &amp;#x26;
    WS_PID=$!
    sleep 3

    echo &quot;启动 Cloudflare 命名隧道 (Claude Code)...&quot;
    cloudflared tunnel --config ~/.cloudflared/config.yml run &amp;#x26;
    CF1_PID=$!
    sleep 3

    echo &quot;&quot;
    echo &quot;==================================&quot;
    echo &quot; Claude Code URL:&quot;
    echo &quot; wss://claude.yourdomain.com&quot;
    echo &quot;==================================&quot;
    echo &quot;&quot;
fi

if [[ &quot;$MODE&quot; == &quot;codex&quot; || &quot;$MODE&quot; == &quot;both&quot; ]]; then
    echo &quot;启动 Codex app-server (端口 9000)...&quot;
    env -u CLAUDECODE codex app-server --listen ws://0.0.0.0:9000 &amp;#x26;
    CODEX_PID=$!
    sleep 2

    echo &quot;启动 Cloudflare 命名隧道 (Codex)...&quot;
    cloudflared tunnel --config ~/.cloudflared/config-codex.yml run &amp;#x26;
    CF2_PID=$!
    sleep 3

    echo &quot;&quot;
    echo &quot;==================================&quot;
    echo &quot; Codex URL:&quot;
    echo &quot; wss://codex.yourdomain.com&quot;
    echo &quot;==================================&quot;
    echo &quot;&quot;
fi

echo &quot;服务已全部启动，按 Ctrl+C 停止&quot;
wait
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果同时运行 Codex，需要额外创建一个隧道和配置文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cloudflared tunnel create agmente-codex
cloudflared tunnel route dns agmente-codex codex.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# ~/.cloudflared/config-codex.yml
tunnel: agmente-codex
credentials-file: ~/.cloudflared/&amp;#x3C;CODEX_TUNNEL_ID&gt;.json

ingress:
  - hostname: codex.yourdomain.com
    service: http://localhost:9000
  - service: http_status:404
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;chmod +x ~/bin/agmente-start.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;停止服务&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pkill -f &quot;stdio-to-ws.*8765&quot;
pkill -f &quot;cloudflared&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;其他代理&lt;/h2&gt;
&lt;p&gt;同样的方式也可以连接其他 ACP 代理：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Gemini CLI
env -u CLAUDECODE npx -y @rebornix/stdio-to-ws --persist --grace-period 604800 \
  &quot;npx @google/gemini-cli --experimental-acp&quot; --port 8765

# Copilot CLI
env -u CLAUDECODE npx -y @rebornix/stdio-to-ws --persist --grace-period 604800 \
  &quot;copilot --acp&quot; --port 8765

# Qwen
env -u CLAUDECODE npx -y @rebornix/stdio-to-ws --persist --grace-period 604800 \
  &quot;qwen --experimental-acp&quot; --port 8765
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;原理解析：这和 SSH 有什么不同？&lt;/h2&gt;
&lt;h3&gt;SSH 的连接方式&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;客户端 ──TCP 连接──&gt; 服务器 22 端口（需要公网 IP）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SSH 是客户端&lt;strong&gt;主动连接&lt;/strong&gt;服务器。服务器必须有公网 IP（或做端口转发），客户端才能找到它。SSH 协议自带加密，用途包括远程终端、文件传输、端口转发等。&lt;/p&gt;
&lt;h3&gt;我们这个方案的连接方式&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iPhone (Agmente)
    │
    │ wss:// (WebSocket over HTTPS)
    ▼
Cloudflare CDN 边缘节点
    │
    │ Cloudflare 内部网络 (QUIC)
    ▼
cloudflared (你的机器上，主动向外连接)
    │
    │ http://localhost:8765
    ▼
stdio-to-ws (WebSocket 桥接)
    │
    │ stdin/stdout (JSON-RPC)
    ▼
Claude Code ACP 适配器
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键区别在于&lt;strong&gt;连接方向反了&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SSH&lt;/strong&gt;：客户端 → 服务器（服务器被动等待连接，需要公网 IP）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloudflare Tunnel&lt;/strong&gt;：服务器 → Cloudflare（服务器主动向外建立隧道，外部请求通过 Cloudflare 转发进来）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就是为什么它能在 WSL2、NAT 后面、甚至校园网里工作——出站连接几乎不受限制，不需要公网 IP，不需要开放端口。&lt;/p&gt;
&lt;h3&gt;各层协议的作用&lt;/h3&gt;
&lt;p&gt;| 层                        | 协议                             | 作用                                  |
| ------------------------- | -------------------------------- | ------------------------------------- |
| Agmente ↔ Cloudflare      | &lt;strong&gt;wss://&lt;/strong&gt;（WebSocket over TLS） | 加密的双向实时通信                    |
| Cloudflare ↔ cloudflared  | &lt;strong&gt;QUIC&lt;/strong&gt;                         | Cloudflare 内部隧道传输，高效穿透 NAT |
| cloudflared ↔ stdio-to-ws | &lt;strong&gt;HTTP → WebSocket&lt;/strong&gt;             | 本地端口转发                          |
| stdio-to-ws ↔ Claude Code | &lt;strong&gt;stdin/stdout&lt;/strong&gt;                 | ACP 协议，JSON-RPC 格式的消息交换     |&lt;/p&gt;
&lt;h3&gt;类比 SSH 反向隧道&lt;/h3&gt;
&lt;p&gt;如果用 SSH 术语来理解，Cloudflare Tunnel 最接近 &lt;strong&gt;SSH 反向隧道&lt;/strong&gt;（&lt;code&gt;ssh -R&lt;/code&gt;）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# SSH 反向隧道：你的机器主动连到中转服务器，把本地端口暴露出去
ssh -R 8765:localhost:8765 中转服务器
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cloudflare Tunnel 本质上就是这个思路，只不过&quot;中转服务器&quot;换成了 Cloudflare 的全球 CDN 网络，自动处理了 TLS 加密、DNS 解析、负载均衡等问题，而且完全免费。&lt;/p&gt;
&lt;h3&gt;wss:// vs ws://&lt;/h3&gt;
&lt;p&gt;类似 HTTPS 和 HTTP 的关系：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ws://&lt;/code&gt; — 明文 WebSocket，数据不加密&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wss://&lt;/code&gt; — WebSocket over TLS，数据加密传输&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cloudflare Tunnel 自动提供 TLS，所以 Agmente 连接时使用的是 &lt;code&gt;wss://&lt;/code&gt;，通信全程加密。&lt;/p&gt;
&lt;h2&gt;进阶：用自己的服务器替代 Cloudflare Tunnel&lt;/h2&gt;
&lt;p&gt;如果你有一台公网服务器，可以完全不依赖 Cloudflare，获得固定 URL 和更低延迟。&lt;/p&gt;
&lt;h3&gt;方式 A：直接部署在公网服务器上&lt;/h3&gt;
&lt;p&gt;把所有服务跑在公网服务器上，用 Caddy 自动签发 TLS 证书：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iPhone (Agmente)
    │
    │ wss://agent.yourdomain.com
    ▼
Caddy (自动 TLS)
    │
    │ http://localhost:8765
    ▼
stdio-to-ws → Claude Code ACP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;1. 在服务器上启动 ACP 桥接：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;env -u CLAUDECODE npx -y @rebornix/stdio-to-ws \
  --persist --grace-period 604800 \
  &quot;npx @zed-industries/claude-code-acp&quot; --port 8765
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 配置 Caddy 反向代理（自动 HTTPS）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# /etc/caddy/Caddyfile
agent.yourdomain.com {
    reverse_proxy localhost:8765
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl restart caddy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Caddy 会自动申请和续期 Let&apos;s Encrypt 证书，Agmente 中填 &lt;code&gt;wss://agent.yourdomain.com&lt;/code&gt; 即可。&lt;/p&gt;
&lt;h3&gt;方式 B：服务器做跳板，Claude Code 留在本地&lt;/h3&gt;
&lt;p&gt;如果你需要 Claude Code 访问本地文件和项目，可以用 SSH 反向隧道把本地端口映射到公网服务器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iPhone (Agmente)
    │
    │ wss://agent.yourdomain.com
    ▼
公网服务器 Caddy/Nginx (TLS 终结)
    │
    │ localhost:8765
    ▼
SSH 反向隧道
    │
    ▼
本地机器 stdio-to-ws → Claude Code ACP (localhost:8765)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;1. 本地启动 ACP 桥接（同前）：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;env -u CLAUDECODE npx -y @rebornix/stdio-to-ws \
  --persist --grace-period 604800 \
  &quot;npx @zed-industries/claude-code-acp&quot; --port 8765
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. 本地建立 SSH 反向隧道：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -R 8765:localhost:8765 your-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这会把本地的 8765 端口映射到服务器的 8765。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 服务器上配置 Caddy（同方式 A）。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这其实就是 Cloudflare Tunnel 干的事——本质都是&quot;本地机器主动向外建立连接，把本地端口暴露出去&quot;。区别只是中转节点从 Cloudflare CDN 换成了你自己的服务器。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;方案对比&lt;/h3&gt;
&lt;p&gt;|                      | Cloudflare 快速隧道 | Cloudflare 命名隧道       | 自有服务器直接部署 | 自有服务器 + SSH 隧道 |
| -------------------- | ------------------- | ------------------------- | ------------------ | --------------------- |
| 需要公网 IP          | 不需要              | 不需要                    | 需要               | 需要（服务器）        |
| 需要域名             | 不需要              | 需要（托管到 Cloudflare） | 需要               | 需要                  |
| URL 固定             | 否（每次变化）      | 是                        | 是                 | 是                    |
| 延迟                 | 较高（经 CDN 中转） | 较高（经 CDN 中转）       | 最低               | 中等                  |
| Claude Code 运行位置 | 本地                | 本地                      | 服务器             | 本地                  |
| 能访问本地文件       | 能                  | 能                        | 不能               | 能                    |
| 配置复杂度           | 最低（零配置）      | 低（一次配置）            | 中等               | 较高                  |&lt;/p&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q: 连接后显示 &quot;Internal error&quot;&lt;/strong&gt;
A: 检查服务端日志，最常见的原因是 &lt;code&gt;CLAUDECODE&lt;/code&gt; 环境变量未清除，导致嵌套会话冲突。确保启动命令包含 &lt;code&gt;env -u CLAUDECODE&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q: 连接后显示 &quot;initialize needed&quot;&lt;/strong&gt;
A: 尝试删除 Agmente 中的 Agent 连接，重新添加并创建新 Session。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q: 隧道 URL 每次都变怎么办？&lt;/strong&gt;
A: 快速隧道（方案 A）的 URL 每次启动都会变。使用命名隧道（方案 B）即可获得固定 URL，只需一个域名（将 DNS 托管到 Cloudflare 免费计划），配置一次后永久生效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q: 想让服务开机自启？&lt;/strong&gt;
A: 可以用 systemd service 管理，或者用 tmux/screen 保持后台运行。&lt;/p&gt;</content:encoded></item><item><title>Tips to improve concentration</title><link>https://blogs-6hn.pages.dev/blog/improve-concentration</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/improve-concentration</guid><description>Mindfulness, cognitive training, and a healthy lifestyle may help sharpen your focus.</description><pubDate>Sat, 10 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { Aside } from &apos;astro-pure/user&apos;&lt;/p&gt;
&lt;p&gt;You&apos;re trying to concentrate, but your mind is wandering or you&apos;re easily distracted. What happened to the laser-sharp focus you once enjoyed? As we age, we tend to have more difficulty filtering out stimuli that are not relevant to the task at hand.&lt;/p&gt;
&lt;h2&gt;What&apos;s fogging up focus?&lt;/h2&gt;
&lt;p&gt;Like a computer that slows with use, the brain accumulates wear and tear that affects processing. This can be caused by a number of physiological stressors such as inflammation, injury to blood vessels (especially if you have high blood pressure), the buildup of abnormal proteins, and naturally occurring brain shrinkage.&lt;/p&gt;
&lt;p&gt;The following factors can also affect your concentration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Underlying conditions.&lt;/strong&gt; Depression or sleep disorders (such as sleep apnea) can undermine your ability to concentrate. So can the effects of vision or hearing loss. You waste precious cognitive resources when you spend too much time trying to make out what&apos;s written on a page or just hear what someone is saying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Medication side effects.&lt;/strong&gt; Some drugs, especially anticholinergics (such as treatments for incontinence, depression, or allergies), can slow processing speed and your ability to think clearly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Excessive drinking.&lt;/strong&gt; Having too much alcohol impairs thinking and causes interrupted sleep, which affects concentration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Information overload.&lt;/strong&gt; We are bombarded with information from TVs, computers, and messages such as texts or emails. When there&apos;s too much material, it burdens our filtering system and it&apos;s easy to get distracted.&lt;/p&gt;
&lt;h2&gt;Strategies to stay focused&lt;/h2&gt;
&lt;p&gt;To improve attention, consider the following strategies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mindfulness.&lt;/strong&gt; &quot;Mindfulness is about focusing attention on the present moment, and practicing mindfulness has been shown to rewire the brain so that attention is stronger in everyday life,&quot; says Kim Willment, a neuropsychologist with Brigham and Women&apos;s Hospital. She recommends sitting still for a few minutes each day, closing your eyes, and focusing on your breathing as well as the sounds and sensations around you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cognitive training.&lt;/strong&gt; Computerized cognitive training games aim to improve your response times and attention. Evidence that this works has been mixed. &quot;The goal of playing these games is not to get better at them, but to get better in the cognitive activities of everyday life,&quot; Willment says. &quot;But there is evidence that a person&apos;s ability to pay attention can be improved by progressively pushing the person to higher levels of performance. So if you reach a certain level of sustained attention, pushing it to the next level can help improve it, and this may translate to everyday life.&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A healthier lifestyle.&lt;/strong&gt; Many aspects of a healthy lifestyle can help attention, starting with sleep and exercise. There is a direct link between exercise and cognitive ability, especially attention. When you exercise, you increase the availability of brain chemicals that promote new brain connections, reduce stress, and improve sleep. And when we sleep, we reduce stress hormones that can be harmful to the brain, and we clear out proteins that injure it.&lt;/p&gt;
&lt;p&gt;Aim for seven to eight hours of sleep each night, and 150 minutes per week of aerobic exercise, such as brisk walking.&lt;/p&gt;
&lt;p&gt;Other healthy steps to improve focus: eat a Mediterranean-style diet, which has been shown to support brain health; treat underlying conditions; and change medications that may be affecting your ability to focus.&lt;/p&gt;
&lt;p&gt;Getting older is out of your control, but healthier living is something you determine, and it may improve concentration.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Article from: &lt;a href=&quot;https://www.health.harvard.edu/mind-and-mood/tips-to-improve-concentration&quot;&gt;Harvard Health Publishing&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><h:img src="/_astro/thumbnail.1GZ294Dz.jpg"/><enclosure url="/_astro/thumbnail.1GZ294Dz.jpg" length="0" type="image/webp"/></item><item><title>Using MDX</title><link>https://blogs-6hn.pages.dev/blog/using-mdx</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/using-mdx</guid><description>Learning how to use MDX in Astro</description><pubDate>Sun, 01 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This theme comes with the &lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/mdx/&quot;&gt;@astrojs/mdx&lt;/a&gt; integration installed and configured in your &lt;code&gt;astro.config.ts&lt;/code&gt; config file. If you prefer not to use MDX, you can disable support by removing the integration from your config file.&lt;/p&gt;
&lt;h2&gt;Why MDX?&lt;/h2&gt;
&lt;p&gt;MDX is a special flavor of Markdown that supports embedded JavaScript &amp;#x26; JSX syntax. This unlocks the ability to &lt;a href=&quot;https://docs.astro.build/en/guides/markdown-content/#mdx-features&quot;&gt;mix JavaScript and UI Components into your Markdown content&lt;/a&gt; for things like interactive charts or alerts.&lt;/p&gt;
&lt;p&gt;If you have existing content authored in MDX, this integration will hopefully make migrating to Astro a breeze.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Here is how you import and use a UI component inside of MDX.&lt;br&gt;
When you open this page in the browser, you should see the clickable button below.&lt;/p&gt;
&lt;p&gt;import { Button } from &apos;astro-pure/user&apos;&lt;/p&gt;
&lt;p&gt;Click Me&lt;/p&gt;
&lt;h2&gt;More Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mdxjs.com/docs/what-is-mdx&quot;&gt;MDX Syntax Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.astro.build/en/guides/markdown-content/#markdown-and-mdx-pages&quot;&gt;Astro Usage Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;a href=&quot;https://docs.astro.build/en/reference/directives-reference/#client-directives&quot;&gt;Client Directives&lt;/a&gt; are still required to create interactive components. Otherwise, all components in your MDX will render as static HTML (no JavaScript) by default.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>What Is 3D Rendering? Complete Guide to 3D Visualization</title><link>https://blogs-6hn.pages.dev/blog/3d-rendering</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/3d-rendering</guid><description>3D imagery has the power to bring cinematic visions to life and help accurately plan tomorrow’s cityscapes. Here, 3D expert Ricardo Ortiz explains how it works.</description><pubDate>Sun, 09 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;3D rendering is all around us. From huge action movies to car commercials to previews of upcoming buildings or product designs, 3D visualization has become so widespread and realistic that you probably don’t even know it’s there.&lt;/p&gt;
&lt;p&gt;In this introductory piece, Chaos’ Ricardo Ortiz explains the basics of 3D rendering, from the computational methods that create imagery to the artistic techniques that create great computer-generated (CG) content and its various uses.&lt;/p&gt;
&lt;h2&gt;What is 3D Rendering?&lt;/h2&gt;
&lt;p&gt;Put simply, 3D rendering is the process of using a computer to generate a 2D image from a digital three-dimensional scene.&lt;/p&gt;
&lt;p&gt;To generate an image, specific methodologies and special software and hardware are used. Therefore, we need to understand that 3D rendering is a process—the one that builds the image.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blogs-6hn.pages.dev/_image?href=%2F_astro%2Fnikola-arsov-still-life-interior-design-vray-3ds-max-05-930px.DoY3_oVo.jpg&amp;#x26;w=1920&amp;#x26;h=1280&amp;#x26;f=webp&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Types of 3D rendering&lt;/h2&gt;
&lt;p&gt;We can create different types of rendered image; they can be realistic or non-realistic.&lt;/p&gt;
&lt;p&gt;A realistic image could be an architectural interior that looks like a photograph, a product-design image such as a piece of furniture, or an automotive rendering of a car. On the other hand, we can create a non-realistic image such as an outline-type diagram or a cartoon-style image with a traditional 2D look. Technically, we can visualize anything we can imagine.&lt;/p&gt;
&lt;h2&gt;How is 3D rendering used?&lt;/h2&gt;
&lt;p&gt;3D rendering is an essential technique for many industries including architecture, product design, advertising, video games and visual effects for film, TV and animation.&lt;/p&gt;
&lt;p&gt;In design and architecture, renders allow creative people to communicate their ideas in a clear and transparent way. A render gives them the chance to evaluate their proposals, experiment with materials, conduct studies and contextualize their designs in the real world before they are built or manufactured.&lt;/p&gt;
&lt;p&gt;For the media and entertainment industries, 3D rendering is fundamental to the creation of sequences and animations that tell stories, whether we’re watching an animated movie, a period drama, or an action sequence with explosions, ships from the future, exotic locales, or extraterrestrial creatures.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blogs-6hn.pages.dev/_image?href=%2F_astro%2Fthanos-dd-single-image-004a.DUX4VGf-.jpg&amp;#x26;w=1920&amp;#x26;h=993&amp;#x26;f=webp&quot; alt=&quot;alt text&quot;&gt;&lt;/p&gt;
&lt;p&gt;Over the past few years, the evolution of computer graphics in these industries has replaced traditional techniques. For example, special effects are being replaced by visual effects, which means stunt people no longer risk their lives in car crashes.&lt;/p&gt;
&lt;p&gt;In advertising, I would dare to say that 90% of automotive commercials are CG—or even more. In the architecture industry, many traditional techniques to create representations, such as scale models, have been replaced with photorealistic imagery to ensure we can see exactly how something will look once it’s built.&lt;/p&gt;
&lt;p&gt;Accelerating processes, reducing costs and the demand for better quality results have helped technology evolve. Hardware is more powerful than ever and the switch to CG was inevitable.&lt;/p&gt;
&lt;h2&gt;How is a 3D rendered image generated?&lt;/h2&gt;
&lt;p&gt;Two pieces of software, with different characteristics, are used to computer-generate images and animations: render engines and game engines. Render engines use a technique called ray tracing, while game engines use a technique called rasterization—and some engines mix both techniques, but we will talk about that later on.&lt;/p&gt;</content:encoded><h:img src="/_astro/thumbnail.DzZDiYKA.jpg"/><enclosure url="/_astro/thumbnail.DzZDiYKA.jpg" length="0" type="image/webp"/></item><item><title>The Impact of Technology on the Music World</title><link>https://blogs-6hn.pages.dev/blog/music-journey</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/music-journey</guid><description>The evolution of music is a symphony of creativity, rhythm, and technology.</description><pubDate>Sat, 30 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The evolution of music is a symphony of creativity, rhythm, and technology. From the humble beginnings of acoustic instruments to the present-day digital era, the relationship between music and technology has been transformative. In this article, we will explore the historical milestones, digital revolution, and emerging technologies that have shaped the music world. Join us on a journey through the chords of innovation as we discuss how technology has changed music.&lt;/p&gt;
&lt;h2&gt;Historical Perspective&lt;/h2&gt;
&lt;p&gt;The marriage of music and technology dates back centuries, with pivotal moments shaping the industry. The invention of the phonograph by Thomas Edison in the late 19th century marked the first time music could be recorded and replayed. Subsequent milestones, such as the electric guitar and the synthesizer, revolutionized music creation, paving the way for new genres and sounds.&lt;/p&gt;
&lt;p&gt;These technological leaps didn&apos;t merely shape the musical landscape of their time but laid a foundation for the continuous evolution of the intersection between music and technology. As artists embraced these innovations, they unlocked new avenues for creativity, paving the way for diverse sounds and genres that have become integral to the vibrant tapestry of the modern music industry. The historical perspective illuminates the symbiotic relationship between music and technology, highlighting the transformative impact that each innovation has had on the way we create, consume, and experience music.&lt;/p&gt;
&lt;h2&gt;Digital Revolution&lt;/h2&gt;
&lt;p&gt;The digital revolution has been a seismic shift in the music industry, altering how music is consumed, distributed, and produced. The transition from physical formats like CDs and vinyl to digital formats such as MP3s and streaming services has democratized access to music. The ease of streaming has transformed how listeners discover and enjoy music, challenging traditional revenue models while offering unparalleled convenience.&lt;/p&gt;
&lt;h2&gt;Technology in Music Consumption and Distribution&lt;/h2&gt;
&lt;p&gt;Streaming services have become the heartbeat of music consumption, causing a decline in traditional music stores. The accessibility of music online has reshaped distribution channels, impacting both artists and record labels. While it provides exposure to a global audience, it also poses challenges regarding fair compensation for artists. The dynamics of the industry are evolving, reflecting the intricate dance between technology and music.
Music Production and Creation&lt;/p&gt;
&lt;p&gt;The advent of digital audio workstations (DAWs), software instruments, and electronic production techniques has democratized music creation. Artists now have powerful tools at their fingertips, enabling them to experiment with sounds, collaborate remotely, and produce music independently. This technological shift has broken down barriers, allowing for a diverse array of voices to be heard in the ever-expanding realm of music.&lt;/p&gt;</content:encoded><h:img src="/_astro/thumbnail.Cx18cRmB.jpg"/><enclosure url="/_astro/thumbnail.Cx18cRmB.jpg" length="0" type="image/webp"/></item><item><title>Markdown Syntax Support</title><link>https://blogs-6hn.pages.dev/blog/markdown</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/markdown</guid><description>Markdown is a lightweight markup language.</description><pubDate>Wed, 26 Jul 2023 08:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Basic Syntax&lt;/h2&gt;
&lt;p&gt;Markdown is a lightweight and easy-to-use syntax for styling your writing.&lt;/p&gt;
&lt;h3&gt;Headers&lt;/h3&gt;
&lt;p&gt;When the content of the article is extensive, you can use headers to segment:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;# Header 1

## Header 2

## Large Header

### Small Header
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Header previews would disrupt the structure of the article, so they are not displayed here.&lt;/p&gt;
&lt;h3&gt;Bold and Italics&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;_Italic text_ and **Bold text**, together will be **_Bold Italic text_**
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Italic text&lt;/em&gt; and &lt;strong&gt;Bold text&lt;/strong&gt;, together will be &lt;strong&gt;&lt;em&gt;Bold Italic text&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Links&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;Text link [Link Name](http://link-url)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;Text link &lt;a href=&quot;http://link-url&quot;&gt;Link Name&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Inline Code&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;This is an `inline code`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;This is an &lt;code&gt;inline code&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Code Blocks&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;```js
// calculate fibonacci
function fibonacci(n) {
  if (n &amp;#x3C;= 1) return 1
  const result = fibonacci(n - 1) + fibonacci(n - 2) // [\!code --]
  return fibonacci(n - 1) + fibonacci(n - 2) // [\!code ++]
}
```
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// calculate fibonacci
function fibonacci(n) {
  if (n &amp;#x3C;= 1) return 1
  const result = fibonacci(n - 1) + fibonacci(n - 2) // [!code --]
  return fibonacci(n - 1) + fibonacci(n - 2) // [!code ++]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Currently using shiki as the code highlighting plugin. For supported languages, refer to &lt;a href=&quot;https://shiki.matsu.io/languages.html&quot;&gt;Shiki: Languages&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Inline Formula&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;This is an inline formula $e^{i\pi} + 1 = 0$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;This is an inline formula $e^{i\pi} + 1 = 0$&lt;/p&gt;
&lt;h3&gt;Formula Blocks&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;$$
\hat{f}(\xi) = \int_{-\infty}^{\infty} f(x) e^{-2\pi i x \xi} \, dx
$$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;$$
\hat{f}(\xi) = \int_{-\infty}^{\infty} f(x) e^{-2\pi i x \xi} , dx
$$&lt;/p&gt;
&lt;p&gt;Currently using KaTeX as the math formula plugin. For supported syntax, refer to &lt;a href=&quot;https://katex.org/docs/supported.html&quot;&gt;KaTeX Supported Functions&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Images&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;![CWorld](https://cravatar.cn/avatar/1ffe42aa45a6b1444a786b1f32dfa8aa?s=200)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cravatar.cn/avatar/1ffe42aa45a6b1444a786b1f32dfa8aa?s=200&quot; alt=&quot;CWorld&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Strikethrough&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;~~Strikethrough~~
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;~~Strikethrough~~&lt;/p&gt;
&lt;h3&gt;Lists&lt;/h3&gt;
&lt;p&gt;Regular unordered list&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;- 1
- 2
- 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1&lt;/li&gt;
&lt;li&gt;2&lt;/li&gt;
&lt;li&gt;3&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Regular ordered list&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;1. GPT-4
2. Claude Opus
3. LLaMa
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;GPT-4&lt;/li&gt;
&lt;li&gt;Claude Opus&lt;/li&gt;
&lt;li&gt;LLaMa&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can continue to nest syntax within lists.&lt;/p&gt;
&lt;h3&gt;Blockquotes&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;&gt; Gunshot, thunder, sword rise. A scene of flowers and blood.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gunshot, thunder, sword rise. A scene of flowers and blood.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can continue to nest syntax within blockquotes.&lt;/p&gt;
&lt;h3&gt;Line Breaks&lt;/h3&gt;
&lt;p&gt;Markdown needs a blank line to separate paragraphs.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;If you don&apos;t leave a blank line
it will be in one paragraph

First paragraph

Second paragraph
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;If you don&apos;t leave a blank line
it will be in one paragraph&lt;/p&gt;
&lt;p&gt;First paragraph&lt;/p&gt;
&lt;p&gt;Second paragraph&lt;/p&gt;
&lt;h3&gt;Separators&lt;/h3&gt;
&lt;p&gt;If you have the habit of writing separators, you can start a new line and enter three dashes &lt;code&gt;---&lt;/code&gt; or asterisks &lt;code&gt;***&lt;/code&gt;. Leave a blank line before and after when there are paragraphs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Advanced Techniques&lt;/h2&gt;
&lt;h3&gt;Inline HTML Elements&lt;/h3&gt;
&lt;p&gt;Currently, only some inline HTML elements are supported, including &lt;code&gt;&amp;#x3C;kdb&gt; &amp;#x3C;b&gt; &amp;#x3C;i&gt; &amp;#x3C;em&gt; &amp;#x3C;sup&gt; &amp;#x3C;sub&gt; &amp;#x3C;br&gt;&lt;/code&gt;, such as&lt;/p&gt;
&lt;h4&gt;Key Display&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;Use &amp;#x3C;kbd&gt;Ctrl&amp;#x3C;/kbd&gt; + &amp;#x3C;kbd&gt;Alt&amp;#x3C;/kbd&gt; + &amp;#x3C;kbd&gt;Del&amp;#x3C;/kbd&gt; to reboot the computer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;Use Ctrl + Alt + Del to reboot the computer&lt;/p&gt;
&lt;h4&gt;Bold Italics&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;&amp;#x3C;b&gt; Markdown also applies here, such as _bold_ &amp;#x3C;/b&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt; Markdown also applies here, such as &lt;em&gt;bold&lt;/em&gt; &lt;/p&gt;
&lt;h3&gt;Other HTML Writing&lt;/h3&gt;
&lt;h4&gt;Foldable Blocks&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;&amp;#x3C;details&gt;&amp;#x3C;summary&gt;Click to expand&amp;#x3C;/summary&gt;It is hidden&amp;#x3C;/details&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;h3&gt;Tables&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;| Header1  | Header2  |
| -------- | -------- |
| Content1 | Content2 |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;| Header1  | Header2  |
| -------- | -------- |
| Content1 | Content2 |&lt;/p&gt;
&lt;h3&gt;Footnotes&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;Use [^footnote] to add a footnote at the point of reference.

Then, at the end of the document, add the content of the footnote (it will be rendered at the end of the article by default).

[^footnote]: Here is the content of the footnote
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;Use [^footnote] to add a footnote at the point of reference.&lt;/p&gt;
&lt;p&gt;Then, at the end of the document, add the content of the footnote (it will be rendered at the end of the article by default).&lt;/p&gt;
&lt;p&gt;[^footnote]: Here is the content of the footnote&lt;/p&gt;
&lt;h3&gt;To-Do Lists&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;- [ ] Incomplete task
- [x] Completed task
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Incomplete task&lt;/li&gt;
&lt;li&gt;[x] Completed task&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Symbol Escaping&lt;/h3&gt;
&lt;p&gt;If you need to use markdown symbols like _ # * in your description but don&apos;t want them to be escaped, you can add a backslash before these symbols, such as &lt;code&gt;\_&lt;/code&gt; &lt;code&gt;\#&lt;/code&gt; &lt;code&gt;\*&lt;/code&gt; to avoid it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;\_Don&apos;t want the text here to be italic\_

\*\*Don&apos;t want the text here to be bold\*\*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Preview:&lt;/p&gt;
&lt;p&gt;_Don&apos;t want the text here to be italic_&lt;/p&gt;
&lt;p&gt;**Don&apos;t want the text here to be bold**&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Embedding Astro Components&lt;/h2&gt;
&lt;p&gt;See &lt;a href=&quot;/docs/integrations/components&quot;&gt;User Components&lt;/a&gt; and &lt;a href=&quot;/docs/integrations/advanced&quot;&gt;Advanced Components&lt;/a&gt; for details.&lt;/p&gt;</content:encoded><h:img src="/_astro/thumbnail.HAXFr_hw.jpg"/><enclosure url="/_astro/thumbnail.HAXFr_hw.jpg" length="0" type="image/webp"/></item><item><title>Markdown 语法支持</title><link>https://blogs-6hn.pages.dev/blog/markdown-zh</link><guid isPermaLink="true">https://blogs-6hn.pages.dev/blog/markdown-zh</guid><description>Markdown 是一种轻量级的「标记语言」。</description><pubDate>Wed, 26 Jul 2023 08:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;基本语法&lt;/h2&gt;
&lt;p&gt;Markdown 是一种轻量级且易于使用的语法，用于为您的写作设计风格。&lt;/p&gt;
&lt;h3&gt;标题&lt;/h3&gt;
&lt;p&gt;文章内容较多时，可以用标题分段：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;# 标题 1

## 标题 2

## 大标题

### 小标题
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;标题预览会打乱文章的结构，所以在此不展示。&lt;/p&gt;
&lt;h3&gt;粗斜体&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;_斜体文本_

**粗体文本**

**_粗斜体文本_**
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;&lt;em&gt;斜体文本&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;粗体文本&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;粗斜体文本&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;链接&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;文字链接 [链接名称](http://链接网址)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;文字链接 &lt;a href=&quot;http://%E9%93%BE%E6%8E%A5%E7%BD%91%E5%9D%80&quot;&gt;链接名称&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;行内代码&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;这是一条 `单行代码`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;这是一条 &lt;code&gt;行内代码&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;代码块&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;```js
// calculate fibonacci
function fibonacci(n) {
  if (n &amp;#x3C;= 1) return 1
  return fibonacci(n - 1) + fibonacci(n - 2)
}
```
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// calculate fibonacci
function fibonacci(n) {
  if (n &amp;#x3C;= 1) return 1
  return fibonacci(n - 1) + fibonacci(n - 2)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当前使用 shiki 作为代码高亮插件，支持的语言请参考 &lt;a href=&quot;https://shiki.matsu.io/languages.html&quot;&gt;shiki / languages&lt;/a&gt;。&lt;/p&gt;
&lt;h3&gt;行内公式&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;这是一条行内公式 $e^{i\pi} + 1 = 0$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;这是一条行内公式 $e^{i\pi} + 1 = 0$&lt;/p&gt;
&lt;h3&gt;公式块&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;$$
\hat{f}(\xi) = \int_{-\infty}^{\infty} f(x) e^{-2\pi i x \xi} \, dx
$$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;$$
\hat{f}(\xi) = \int_{-\infty}^{\infty} f(x) e^{-2\pi i x \xi} , dx
$$&lt;/p&gt;
&lt;p&gt;当前使用 KaTeX 作为数学公式插件，支持的语法请参考 &lt;a href=&quot;https://katex.org/docs/supported.html&quot;&gt;KaTeX Supported Functions&lt;/a&gt;。&lt;/p&gt;
&lt;h4&gt;图片&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;![CWorld](https://cravatar.cn/avatar/1ffe42aa45a6b1444a786b1f32dfa8aa?s=200)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cravatar.cn/avatar/1ffe42aa45a6b1444a786b1f32dfa8aa?s=200&quot; alt=&quot;CWorld&quot;&gt;&lt;/p&gt;
&lt;h4&gt;删除线&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;~~删除线~~
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;~~删除线~~&lt;/p&gt;
&lt;h3&gt;列表&lt;/h3&gt;
&lt;p&gt;普通无序列表&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;- 1
- 2
- 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1&lt;/li&gt;
&lt;li&gt;2&lt;/li&gt;
&lt;li&gt;3&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;普通有序列表&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;1. GPT-4
2. Claude Opus
3. LLaMa
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;GPT-4&lt;/li&gt;
&lt;li&gt;Claude Opus&lt;/li&gt;
&lt;li&gt;LLaMa&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;列表里可以继续嵌套语法&lt;/p&gt;
&lt;h3&gt;引用&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;&gt; 枪响，雷鸣，剑起。繁花血景。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;枪响，雷鸣，剑起。繁花血景。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;引用里也可以继续嵌套语法。&lt;/p&gt;
&lt;h3&gt;换行&lt;/h3&gt;
&lt;p&gt;markdown 分段落是需要空一行的。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;如果不空行
就会在一段

第一段

第二段
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;如果不空行
就会在一段&lt;/p&gt;
&lt;p&gt;第一段&lt;/p&gt;
&lt;p&gt;第二段&lt;/p&gt;
&lt;h3&gt;分隔符&lt;/h3&gt;
&lt;p&gt;如果你有写分割线的习惯，可以新起一行输入三个减号&lt;code&gt;---&lt;/code&gt; 或者星号 &lt;code&gt;***&lt;/code&gt;。当前后都有段落时，请空出一行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;高级技巧&lt;/h2&gt;
&lt;h3&gt;行内 HTML 元素&lt;/h3&gt;
&lt;p&gt;目前只支持部分段内 HTML 元素效果，包括 &lt;code&gt;&amp;#x3C;kdb&gt; &amp;#x3C;b&gt; &amp;#x3C;i&gt; &amp;#x3C;em&gt; &amp;#x3C;sup&gt; &amp;#x3C;sub&gt; &amp;#x3C;br&gt;&lt;/code&gt; ，如&lt;/p&gt;
&lt;h4&gt;键位显示&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;使用 &amp;#x3C;kbd&gt;Ctrl&amp;#x3C;/kbd&gt; + &amp;#x3C;kbd&gt;Alt&amp;#x3C;/kbd&gt; + &amp;#x3C;kbd&gt;Del&amp;#x3C;/kbd&gt; 重启电脑
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;使用 Ctrl + Alt + Del 重启电脑&lt;/p&gt;
&lt;h4&gt;粗斜体&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;&amp;#x3C;b&gt; Markdown 在此处同样适用，如 _加粗_ &amp;#x3C;/b&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt; Markdown 在此处同样适用，如 &lt;em&gt;加粗&lt;/em&gt; &lt;/p&gt;
&lt;h3&gt;其他 HTML 写法&lt;/h3&gt;
&lt;h4&gt;折叠块&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;&amp;#x3C;details&gt;&amp;#x3C;summary&gt;点击展开&amp;#x3C;/summary&gt;它被隐藏了&amp;#x3C;/details&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;h3&gt;表格&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;| 表头1 | 表头2 |
| ----- | ----- |
| 内容1 | 内容2 |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;| 表头1 | 表头2 |
| ----- | ----- |
| 内容1 | 内容2 |&lt;/p&gt;
&lt;h3&gt;注释&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;在引用的地方使用 [^注释] 来添加注释。

然后在文档的结尾，添加注释的内容（会默认于文章结尾渲染之）。

[^注释]: 这里是注释的内容
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;在引用的地方使用 &lt;a href=&quot;%E8%BF%99%E9%87%8C%E6%98%AF%E6%B3%A8%E9%87%8A%E7%9A%84%E5%86%85%E5%AE%B9&quot;&gt;^注释&lt;/a&gt; 来添加注释。&lt;/p&gt;
&lt;p&gt;然后在文档的结尾，添加注释的内容（会默认于文章结尾渲染之）。&lt;/p&gt;
&lt;h3&gt;To-Do 列表&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;- [ ] 未完成的任务
- [x] 已完成的任务
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[ ] 未完成的任务&lt;/li&gt;
&lt;li&gt;[x] 已完成的任务&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;符号转义&lt;/h3&gt;
&lt;p&gt;如果你的描述中需要用到 markdown 的符号，比如 _ # * 等，但又不想它被转义，这时候可以在这些符号前加反斜杠，如 &lt;code&gt;\_&lt;/code&gt; &lt;code&gt;\#&lt;/code&gt; &lt;code&gt;\*&lt;/code&gt; 进行避免。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;\_不想这里的文本变斜体\_

\*\*不想这里的文本被加粗\*\*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预览：&lt;/p&gt;
&lt;p&gt;_不想这里的文本变斜体_&lt;/p&gt;
&lt;p&gt;**不想这里的文本被加粗**&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;内嵌 Astro 组件&lt;/h2&gt;
&lt;p&gt;See &lt;a href=&quot;/docs/integrations/components&quot;&gt;User Components&lt;/a&gt; and &lt;a href=&quot;/docs/integrations/advanced&quot;&gt;Advanced Components&lt;/a&gt; for details.&lt;/p&gt;</content:encoded><h:img src="/_astro/thumbnail.HAXFr_hw.jpg"/><enclosure url="/_astro/thumbnail.HAXFr_hw.jpg" length="0" type="image/webp"/></item></channel></rss>