Skip to main content

60 posts tagged with "编程"

View All Tags

  • 旁边增加一个目录
    • 需要下载一些网上有的 template 但是巨丑
  • 夜间模式
  • 手机适配

1. 创建 D:\tmp\demo.md

# 我的博客

- 参考
- [Pandoc](https://pandoc.org/)
- [Classless CSS](https://github.com/dbohdan/classless-css)

## 关于

这是一个用 Pandoc 批量生成多主题 HTML 的博客示例。

## 代码

```powershell
pandoc demo.md -s -c $css -o blog.html

主题

主题说明
simple极简优雅
githubGitHub 风格
water干净清爽
sakura樱花粉
latex学术排版

## 2. 创建 `D:\tmp\md-to-html.ps1`

```powershell
$inputFile = 'D:\tmp\demo.md'
$outputDir = 'D:\tmp\blog'
$openInBrowser = $true # 改成 $false 则不自动打开

if (!(Get-Command pandoc -ErrorAction SilentlyContinue)) {
Write-Host "错误:未找到 pandoc" -ForegroundColor Red
return
}

if (!(Test-Path $inputFile)) {
Write-Host "错误:未找到输入文件 $inputFile" -ForegroundColor Red
return
}

if (!(Test-Path $outputDir)) { New-Item -ItemType Directory -Path $outputDir | Out-Null }

$themes = @(
# ===== 基础款 =====
@{ Name = "simple"; Css = "https://cdn.jsdelivr.net/npm/simple.css@1.6.1/dist/simple.min.css"; Highlight = "pygments"; Desc = "Simple.css - 极简优雅,默认蓝色链接" },
@{ Name = "shadcn"; Css = "https://cdn.jsdelivr.net/gh/fordus/shadcn-classless@main/dist/shadcn-classless.css"; Highlight = "pygments"; Desc = "shadcn-classless - 现代设计感,蓝色主色" },
@{ Name = "devcss"; Css = "https://cdn.jsdelivr.net/npm/@intergrav/dev.css@5"; Highlight = "pygments"; Desc = "dev.css - 轻量清新,蓝色调" },
@{ Name = "github"; Css = "https://cdn.jsdelivr.net/npm/github-markdown-css/github-markdown.css"; Highlight = "pygments"; Desc = "GitHub Markdown - 复刻 GitHub 风格" },
@{ Name = "pico"; Css = "https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css"; Highlight = "pygments"; Desc = "Pico.css - 功能丰富,20 种配色" },
@{ Name = "water"; Css = "https://cdn.jsdelivr.net/npm/water.css@2/out/water.css"; Highlight = "pygments"; Desc = "Water.css - 干净极简,蓝色主色" },
@{ Name = "sucss"; Css = "https://speyll.github.io/suCSS/suCSS-min.css"; Highlight = "pygments"; Desc = "suCSS - 简约优雅,本地字体" },
@{ Name = "edible"; Css = "https://cdn.jsdelivr.net/npm/@svmukhin/edible-css@latest/dist/edible.min.css"; Highlight = "pygments"; Desc = "EdibleCSS - 即插即用,干净现代" },
@{ Name = "marx"; Css = "https://cdn.jsdelivr.net/npm/marx-css@5/css/marx.min.css"; Highlight = "pygments"; Desc = "Marx - 极简主义,零优先级" },
@{ Name = "aveccss"; Css = "https://cdn.jsdelivr.net/gh/bk/aveccss@main/dist/aveccss.min.css"; Highlight = "pygments"; Desc = "AvecCSS - 轻量模块化" },
@{ Name = "nocss"; Css = "https://unpkg.com/nocss-framework@latest/dist/nocss.min.css"; Highlight = "pygments"; Desc = "NoCSS - 智能检测 HTML 结构" },

# ===== 新增经典款 =====
@{ Name = "awsm"; Css = "https://cdn.jsdelivr.net/npm/awsm.css@3.0.7/awsm.min.css"; Highlight = "pygments"; Desc = "awsm.css - 语义化样式,阅读友好" },
@{ Name = "sakura"; Css = "https://cdn.jsdelivr.net/npm/sakura.css@1.4.1/sakura.min.css"; Highlight = "pygments"; Desc = "Sakura - 樱花粉柔色调,极简" },
@{ Name = "new"; Css = "https://cdn.jsdelivr.net/npm/new.css@1.1.3/new.min.css"; Highlight = "pygments"; Desc = "new.css - 零类,轻量" },
@{ Name = "mvp"; Css = "https://cdn.jsdelivr.net/npm/mvp.css@1.12.0/mvp.min.css"; Highlight = "pygments"; Desc = "MVP.css - 专为原型设计,语义化" },
@{ Name = "vanilla"; Css = "https://cdn.jsdelivr.net/npm/vanilla-framework@3.0.1/build/css/vanilla.min.css"; Highlight = "pygments"; Desc = "Vanilla Framework - Ubuntu 风格,classless 也支持" },
@{ Name = "tufte"; Css = "https://cdn.jsdelivr.net/npm/tufte-css@1.8.0/tufte.min.css"; Highlight = "pygments"; Desc = "Tufte CSS - 爱德华·塔夫特排版风格" },
@{ Name = "latex"; Css = "https://cdn.jsdelivr.net/npm/latex.css@1.12.0/latex.min.css"; Highlight = "pygments"; Desc = "LaTeX.css - 模拟 LaTeX 排版" },
@{ Name = "bamboo"; Css = "https://cdn.jsdelivr.net/npm/bamboo.css@2.0.0/bamboo.min.css"; Highlight = "pygments"; Desc = "Bamboo.css - 清新绿色调" },
@{ Name = "paper"; Css = "https://cdn.jsdelivr.net/npm/papercss@1.9.2/dist/paper.min.css"; Highlight = "pygments"; Desc = "PaperCSS - 手写纸质感" },
@{ Name = "mini"; Css = "https://cdn.jsdelivr.net/npm/mini.css@4.0.0/dist/mini.min.css"; Highlight = "pygments"; Desc = "mini.css - 小巧,响应式" },

# ===== 更多类·无类 =====
@{ Name = "kacss"; Css = "https://cdn.jsdelivr.net/npm/kacss@2.0.0/kacss.min.css"; Highlight = "pygments"; Desc = "kaCSS - 极简,纯语义" },
@{ Name = "splendor"; Css = "https://cdn.jsdelivr.net/gh/splendor-css/splendor@main/dist/splendor.min.css"; Highlight = "pygments"; Desc = "Splendor - 优雅,深色主题自适应" },
@{ Name = "fruum"; Css = "https://cdn.jsdelivr.net/gh/fruumio/fruum-css@master/fruum.min.css"; Highlight = "pygments"; Desc = "Fruum - 专注可读性" },
@{ Name = "emerick"; Css = "https://cdn.jsdelivr.net/gh/emerick-css/emerick@latest/emerick.min.css"; Highlight = "pygments"; Desc = "Emerick - 极简,现代" },
@{ Name = "primitive"; Css = "https://cdn.jsdelivr.net/npm/primitive@1.0.0/primitive.min.css"; Highlight = "pygments"; Desc = "Primitive - 移动优先,简约" },
@{ Name = "sus"; Css = "https://cdn.jsdelivr.net/gh/sus-css/sus@main/sus.min.css"; Highlight = "pygments"; Desc = "Sus - 轻量,强调内容" },
@{ Name = "stylize"; Css = "https://cdn.jsdelivr.net/gh/stylize-css/stylize@main/stylize.min.css"; Highlight = "pygments"; Desc = "Stylize - 风格化,多种颜色变量" },
@{ Name = "pattern"; Css = "https://cdn.jsdelivr.net/gh/pattern-css/pattern@latest/pattern.min.css"; Highlight = "pygments"; Desc = "Pattern - 模块化,可定制" },
@{ Name = "system"; Css = "https://cdn.jsdelivr.net/npm/system.css@1.0.1/system.min.css"; Highlight = "pygments"; Desc = "System.css - 模仿操作系统原生样式" },
@{ Name = "splendid"; Css = "https://cdn.jsdelivr.net/gh/splendid-css/splendid@main/splendid.min.css"; Highlight = "pygments"; Desc = "Splendid - 干净,可选强调色" },

# ===== 偏重 UI 但也可以 classless 使用 =====
@{ Name = "bulma"; Css = "https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css"; Highlight = "pygments"; Desc = "Bulma - 虽然需要类,但基础标签仍有样式" },
@{ Name = "milligram"; Css = "https://cdn.jsdelivr.net/npm/milligram@1.4.1/dist/milligram.min.css"; Highlight = "pygments"; Desc = "Milligram - 极简,但需少量类" },
@{ Name = "spectre"; Css = "https://cdn.jsdelivr.net/npm/spectre.css@0.5.9/dist/spectre.min.css"; Highlight = "pygments"; Desc = "Spectre - 轻量,可作基础样式" },
@{ Name = "blaze"; Css = "https://cdn.jsdelivr.net/npm/blaze@4.0.2/dist/blaze.min.css"; Highlight = "pygments"; Desc = "Blaze - 原子化,但默认标签有样式" },
@{ Name = "kutty"; Css = "https://cdn.jsdelivr.net/npm/kutty@0.6.0/dist/kutty.min.css"; Highlight = "pygments"; Desc = "Kutty - 面向内容,类似水" },
@{ Name = "cirrus"; Css = "https://cdn.jsdelivr.net/npm/cirrus-ui@0.6.0/dist/cirrus.min.css"; Highlight = "pygments"; Desc = "Cirrus - 现代组件,基础样式仍可用" }
)

# 过滤掉 CSS 链接无法访问的主题
$themes = $themes | Where-Object {
$name = $_.Name
$css = $_.Css
try {
$response = Invoke-WebRequest -Uri $css -Method Head -TimeoutSec 5 -ErrorAction Stop
if ($response.StatusCode -eq 200) {
Write-Host "✓ $name - CSS 可访问" -ForegroundColor Green
$true
} else {
Write-Host "✗ $name - CSS 返回状态码 $($response.StatusCode)" -ForegroundColor Yellow
$false
}
} catch {
Write-Host "✗ $name - CSS 无法访问" -ForegroundColor Red
$false
}
}

if ($themes.Count -eq 0) {
Write-Host "没有可用的主题,请检查网络或稍后重试。" -ForegroundColor Red
return
}

$indexItems = @()
foreach ($t in $themes) {
$outputFile = Join-Path $outputDir "blog-$($t.Name).html"
Write-Host "生成 $($t.Name)" -ForegroundColor Cyan
pandoc $inputFile -s -c $t.Css --highlight-style $t.Highlight --metadata title="博客 - $($t.Desc)" -o $outputFile
$indexItems += "<li><a href='blog-$($t.Name).html'>$($t.Name)</a> - $($t.Desc)</li>"
}

$indexHtml = "<!DOCTYPE html><html><head><meta charset='utf-8'><title>博客主题</title></head><body><h1>博客主题预览</h1><ul>$($indexItems -join '')</ul></body></html>"
Set-Content -Path (Join-Path $outputDir "index.html") -Value $indexHtml -Encoding UTF8

Write-Host "完成,共 $($themes.Count) 个页面" -ForegroundColor Green

if ($openInBrowser) {
foreach ($t in $themes) {
$htmlFile = Join-Path $outputDir "blog-$($t.Name).html"
Start-Process $htmlFile
}
Start-Process (Join-Path $outputDir "index.html")
}

3. 运行

pwsh "D:\tmp\md-to-html.ps1"
  • 或者直接在 PowerShell 里:
D:\tmp\md-to-html.ps1
  • 生成后在 D:\tmp\blog\ 下会有 index.html 和 多个不同主题的博客页面。
编程5 min read

本文档列出 ZooCrush 项目中 Claude Code 可用的所有技能(Skills)及其用途。


项目核心技能

/project-assistant

用途:Godot 项目通用助手,处理功能实现、bug 修复、文档同步和任务管理。 特点

  • 总是先澄清模糊需求
  • 为新功能和修复编写测试
  • 保持项目结构文档更新 触发:处理项目任务时的默认行为(CLAUDE.md 中已配置)

开发流程技能

/prototype

用途:快速原型开发,验证想法和概念。 适合:探索新功能、测试游戏机制、验证技术方案。

/tdd

用途:测试驱动开发。 流程:先写测试 → 实现功能 → 验证测试通过。 适合:确保代码质量、防止回归 bug。

/qa

用途:质量保证检查。 检查项:代码规范、性能、安全性、边界情况。

/review

用途:代码审查。 审查维度:正确性、可读性、性能、架构一致性。

/code-review

用途:审查当前 diff,查找 bug 和优化点。 参数--comment(发布 PR 评论)、--fix(直接修复)。 等级:low/medium/high/max(覆盖范围)。

/simplify

用途:简化代码,消除重复,提升效率。 关注:代码质量和可维护性,不查找 bug。

/verify

用途:验证代码修改是否真正实现了预期功能。 方式:运行应用并观察实际行为。


架构与设计技能

/codebase-design

用途:分析和设计代码库架构。 输出:架构图、模块关系、改进建议。

/domain-modeling

用途:建立和扩展领域模型。 关联:读取并更新 CONTEXT.md

/design-an-interface

用途:设计接口和 API。 适合:定义模块边界、设计公共接口。

/diagnosing-bugs

用途:诊断和修复 bug。 流程:分析症状 → 定位原因 → 提出修复方案。 关联:读取 CONTEXT.md 和 ADR 理解系统结构。

/improve-codebase-architecture

用途:改进代码库架构。 输出:重构建议、架构优化方案。 关联:遵循 CONTEXT.md 和 ADR 的决策。

/request-refactor-plan

用途:请求重构计划。 流程:分析代码 → 提出重构方案 → 等待批准。


任务管理技能

/to-prd

用途:将想法转换为产品需求文档 (PRD)。 输出.scratch/<feature>/PRD.md内容:功能描述、用户故事、验收标准。

/to-issues

用途:将 PRD 拆分为具体实现任务。 输出.scratch/<feature>/issues/<NN>-<slug>.md关联:读取 docs/agents/issue-tracker.md

/triage

用途:处理和分类问题。 流程:评估 → 分类 → 应用标签。 标签:needs-triage、needs-info、ready-for-agent、ready-for-human、wontfix。 关联:读取 docs/agents/triage-labels.md

/grill-me

用途:深入追问,澄清需求。 适合:开始实现前的需求确认。

/grill-with-docs

用途:基于文档深入追问。 关联:读取 CONTEXT.md 和相关文档。

/grilling

用途:通用追问技能,支持多种追问模式。


Git 工作流技能

/git-guardrails-claude-code

用途:Git 安全检查,防止危险操作。 检查:分支保护、提交验证、推送安全。

/resolving-merge-conflicts

用途:解决合并冲突。 流程:分析冲突 → 提出解决方案 → 执行合并。


辅助技能

/obsidian-vault

用途:Obsidian 笔记库操作。 适合:文档管理、知识库更新。

/migrate-to-shoehorn

用途:迁移到 Shoehorn 模式。 适合:架构重构、代码迁移。

/scaffold-exercises

用途:生成练习和示例代码。 适合:学习、教学、演示。

/setup-pre-commit

用途:配置 pre-commit hooks。 检查:代码风格、测试、静态分析。

/teach

用途:教学和知识传递。 适合:解释代码、教授概念、团队培训。

/wizard

用途:交互式向导,逐步引导完成任务。 适合:复杂流程、新手引导。

/handoff

用途:任务交接文档。 适合:切换上下文、交接工作、异步协作。


工具技能

/init

用途:初始化新的 CLAUDE.md 文件。 适合:新项目、项目迁移。

/update-config

用途:更新 Claude Code 配置 (settings.json)。 配置项:权限、环境变量、hooks。

/keybindings-help

用途:自定义键盘快捷键。 配置~/.claude/keybindings.json

/fewer-permission-prompts

用途:减少权限提示。 方式:扫描常用工具调用,添加允许列表。

/trigger-cost-savings

用途:触发成本优化模式。

/token-efficiency

用途:Token 优化最佳实践。 内容:高效文件读取、命令执行、输出处理策略。

/run

用途:启动和运行项目应用。 适合:验证修改、测试功能。

/loop

用途:循环执行任务。 参数:间隔时间、任务内容。 适合:定期检查、持续监控。


研究与搜索技能

/deep-research

用途:深度研究,多源搜索,验证结论。 流程:搜索 → 获取源 → 验证 → 综合报告。 适合:需要事实依据的技术决策。

/find-skills

用途:发现和安装新技能。 适合:查找特定功能的技能。


安全与验证技能

/security-review

用途:安全审查。 检查:漏洞、风险、安全最佳实践。


写作技能

/edit-article

用途:编辑文章和文档。

/writing-beats

用途:写作节奏和结构。

/writing-fragments

用途:写作片段管理。

/writing-great-skills

用途:编写高质量技能文档。

/writing-shape

用途:写作形式和风格。


决策与规划技能

/decision-mapping

用途:决策映射,可视化决策路径。

/ubiquitous-language

用途:建立通用语言,统一术语。


API 参考

/claude-api

用途:Claude API 参考。 内容:模型 ID、定价、参数、流式、工具调用、MCP、缓存。


技能使用示例

开发新功能

1. /to-prd → 创建产品需求文档
2. /to-issues → 拆分为具体任务
3. /triage → 分类和优先级排序
4. /tdd → 测试驱动开发
5. /verify → 验证功能正确性
6. /qa → 质量检查

修复 Bug

1. /diagnosing-bugs → 诊断问题
2. /grill-me → 澄清细节
3. /tdd → 编写测试重现 bug
4. 修复代码
5. /verify → 验证修复有效
6. /code-review → 审查修复代码

改进架构

1. /improve-codebase-architecture → 分析和提出改进
2. /request-refactor-plan → 制定重构计划
3. /domain-modeling → 更新领域模型
4. 创建新 ADR 记录决策

项目配置文件

技能会读取以下配置文件:

文件被读取的技能
CLAUDE.md所有技能(默认行为)
CONTEXT.md/domain-modeling/diagnosing-bugs/tdd/improve-codebase-architecture
docs/adr/*.md/improve-codebase-architecture/diagnosing-bugs
docs/agents/issue-tracker.md/to-issues/triage/to-prd/qa
docs/agents/triage-labels.md/triage
docs/agents/domain.md所有读取领域文档的技能
docs/project-structure.md/project-assistant(开发前必读)

注意事项

  1. 默认行为:所有任务会先触发 /project-assistant(CLAUDE.md 配置)
  2. 文档优先:修改代码前应先读取 docs/project-structure.md
  3. 领域一致性:使用领域术语时应参考 CONTEXT.md
  4. 架构决策:修改架构时应检查相关 ADR,避免违反已有决策
  5. 本地追踪:问题和任务存储在 .scratch/<feature>/ 目录

文档生成时间:2026-06-30

编程6 min read

windows powershell

winget install junegunn.fzf
Install-Module -Name PSFzf -Scope CurrentUser -Force

notepad $PROFILE
# 贴贴内容 也可以先测试一下
Import-Module PSFzf
Set-PsFzfOption -PSReadlineChordProvider 'Ctrl+t' -PSReadlineChordReverseHistory 'Ctrl+r'

编程One min read

1. 设备识别:分清“真网卡”与“虚网卡”

在监控列表中,你会看到多种设备,统计外网流量时必须过滤:

设备前缀类型说明统计建议
enp... / eth...物理网卡服务器接路由器的网线接口。包含 上网流量 + 局域网互传主统计对象
br-...Docker 网桥每个 Docker 网络(Project)的汇总口。选看(查具体业务)
veth...容器虚线单个容器的流量。数量极多。建议过滤排除

💡 技巧:查找 br-xxxx 对应哪个 Docker 项目: docker network ls --no-trunc | grep <ID 前 12 位>


2. 核心 PromQL 表达式

计算**总流量(GB)**的通用公式:

(
sum(increase(node_network_receive_bytes_total{instance="192.168.31.2:9100", device=~"enp2s0|wlp1s0"}[$__range]))
+
sum(increase(node_network_transmit_bytes_total{instance="192.168.31.2:9100", device=~"enp2s0|wlp1s0"}[$__range]))
) / 1024 / 1024 / 1024
  • increase(...[$__range]):计算选定时间段内的增量。它会自动处理服务器重启导致的计数器归零(Counter Reset)。
  • device=~"enp2s0|wlp1s0":使用正则只选物理网卡,排除 br-veth,避免重复计算。
  • / 1024 / 1024 / 1024:将单位从 Bytes 转换为 GB。

3. 如何实现“自然月”统计表格?

Prometheus 擅长看“过去 30 天”,如果要看“1 月、2 月”这种账单报表,需在 Grafana 做以下配置:

  1. 面板选择:使用 Table 视图。
  2. Query Options:将 Min interval 设置为 1M(强制按月聚合)。
  3. Transform (转换)
    • 添加 "Group by"
      • Time 列:选 Group by,间隔选 1M
      • Value 列:选 Calculate -> Total
  4. 时间范围:右上角选择 Last 6 months,即可看到逐月对比。

4. 常见问题 (FAQ)

  • Q: 为什么查不到月初的数据?
    • A: 检查 Prometheus 启动参数 --storage.tsdb.retention.time。默认仅 15 天,建议改为 90d 或更久。
  • Q: first_over_time 报错怎么办?
    • A: 原生 Prometheus 不支持此函数。可用 min_over_time(...[30d]) 替代,因为流量计数器是递增的,区间最小值就是该区间的起点值。
  • Q: 流量准吗?
    • A: enp2s0 包含局域网内(如手机访问服务器)的流量。若要绝对准确的“光猫账单”,建议在路由器的 WAN 口部署监控。

5. 进阶:网络链路质量参考

如果流量正常但访问慢,需配合以下工具排查运营商(移动/联通)线路:

  • mtr -rw <IP>:看每一跳的丢包率(Loss%)。
  • nexttrace <IP>:看流量经过了哪些城市的运营商网关。

编程2 min read

大型语言模型 (LLM) 很强大,但与外部数据和工具交互不便。模型上下文协议 (MCP) 旨在解决此问题,让 AI 从“解答者”变为“执行者”。本文是 MCP 的快速入门笔记。

参考

MCP 解决了啥痛点? 🤔

  • 传统痛点:

    • LLM 无法感知实时数据、操作外部工具。
    • 手动为 LLM 准备上下文,效率低,自动化程度差。
    • 不同 LLM 平台的 Function Calling 实现不兼容。
  • MCP 方案:

    • MCP 提供标准接口(像 AI 的“USB-C”),让应用和 LLM 标准化地交换上下文(数据与工具调用)。
  • 图解 MCP

  • MCP 使 LLM 能按需、安全、灵活地使用外部工具和数据。

快速上手 MCP:Github MCP 示例 🚀

以孟哥博客中 Github MCP + Cursor (AI IDE) 为例:

  • Github 令牌 🔑

  • 路径:Github -> Settings -> Developer settings -> Personal access tokens -> Tokens (classic).

  • 权限:授予 repoworkflow

  • 注意: 妥善保管 Token。

  • 配置 MCP 服务器 (Cursor) ⚙️

    • 找到应用的 MCP 配置文件 (如 .mcp/config.json)。

    • 将 Github Access Token 添加进去。

    • 示例配置:

    {
    "inputs": [
    {
    "type": "promptString",
    "id": "github_token",
    "description": "GitHub Personal Access Token",
    "password": true
    }
    ],
    "servers": {
    "GitHub": {
    "command": "npx",
    "args": [
    "-y",
    "@modelcontextprotocol/server-github"
    ],
    "env": {
    "GITHUB_PERSONAL_ACCESS_TOKEN": "${input:github_token}"
    }
    }
    }
    }
  • 验证与使用 🗣️

    • 重启应用 (如 Cursor),检查 MCP 连接状态。
    • 成功后,即可用自然语言操作代码库。例如:“总结 feature-xyz 分支的提交”。
    • 测试问题:查找 我 github 上 crawler-utils 项目 下有什么内容

报错

Connection state: Error spawn npx ENOENT

{
// 💡 Inputs are prompted on first server start, then stored securely by VS Code.
"inputs": [
{
"type": "promptString",
"id": "perplexity-key",
"description": "Perplexity API Key",
"password": true
}
],
"servers": {
// https://github.com/ppl-ai/modelcontextprotocol/
"Perplexity": {
"type": "stdio",
"command": "npx",
"args": ["-y", "server-perplexity-ask"],
"env": {
"PERPLEXITY_API_KEY": "${input:perplexity-key}"
}
}
}
}
  • 本地启动不报错 npx -y @modelcontextprotocol/server-github
2025-05-15 17:59:37.349 [info] Connection state: Starting
2025-05-15 17:59:37.354 [info] Starting server from LocalProcess extension host
2025-05-15 17:59:37.366 [info] Connection state: Starting
2025-05-15 17:59:37.366 [info] Connection state: Error spawn npx ENOENT
  • 改成
{  
"servers": {
"github": {
"command": "cmd",
"args": ["/c","npx","-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${input:github_token}"
}
}
}
}

报错 Server exited before responding to initialize request

2025-05-15 18:02:47.180 [info] Connection state: Running
2025-05-15 18:02:47.196 [warning] [server stderr] 'npx' �����ڲ����ⲿ���Ҳ���ǿ����еij���
2025-05-15 18:02:47.196 [warning] [server stderr] �����������
2025-05-15 18:02:47.200 [info] Connection state: Error Process exited with code 1
2025-05-15 18:02:47.200 [error] Server exited before responding to `initialize` request.
  • 首先直接搜搜不到结果
  • 然后本地启动也没问题
  • 然后想尝试看看错误日志是什么
    • 应该是 GBK 编码
    • 但是输出的时候应该已经丢失内容了
    • C:\Users\z\AppData\Roaming\Code\logs\20250515T181527\window1 下也不能看到原内容
  • 在 Trae 上有直接的 MCP 市场,可以直接配置成功,就放弃了
    • 等别人出现同样的问题把
编程4 min read

需求

  • Linux 连续解压 多个文件 unzip 命令 进度条 显示解压进度

pv

  • 安装
    • sudo apt-get install parallel pv
  • 使用
    • ls L15-*.zip | parallel -j0 'pv | unzip
    • 报错
      • UnZip 6.00 of 20 April 2009, by Debian. Original by Info-ZIP.
      • Usage: unzip [-Z] [-opts[modifiers]] file[.zip] [list] [-x xlist] [-d exdir]
    • ls L13-*.zip | parallel -j1 'echo "正在解压:"; unzip -o ; echo "✓ 完成"'
    • 也没有进度条

7zip

  • sudo apt install p7zip-full p7zip-rar
  • 7z x L15-2.zip
  • for arc in M479*; do 7z x "$arc" ; done

alias

_do_up_zip_all() {
setopt local_options nullglob
local files=(*.zip *.rar *.7z)

if [ ${#files[@]} -eq 0 ]; then
echo "未发现压缩文件,跳过。"
return
fi

for arc in "${files[@]}"; do
if [ -f "$arc" ]; then
echo "正在处理:$arc"
if 7z x "$arc" -y; then
echo "解压成功,删除源文件。.."
rm "$arc"
else
echo "警告:$arc 解压失败,保留原文件!"
fi
fi
done
}

alias unzip_all='_do_up_zip_all'
编程One min read

  • 使用了默认用户数据文件夹 user_cookies = os.path.join(os.path.expanduser("~"), r"AppData\Local\Google\Chrome\User Data\Default")

    • option.add_argument("--user-data-dir={}".format(user_cookies))
    • 注销这条就能用
    • 开启就会启动 chrome 但是程序无法控制
    • 过一段时间后报错
  • 版本不一样

    • chrome version 136.0.7103.93
    • chromedriver version 136.0.7103.92
  • 报错 session not created: probably user data directory is already in use, please specify a unique value for --user-data-dir argument, or don't use --user-data-dir

  • 报错 session not created: DevToolsActivePort file doesn't exist

  • 猜想可能是 某个程序占用了 C:\Users\z7407\AppData\Local\Google\Chrome\User Data

    • handle64.exe -a "C:\YourFolder"
    • 下载 handle 地址
    • 无进程占用
  • from webdriver_manager.chrome import ChromeDriverManager

    • 这个是 driver 的版本管理器,不是 chrome 的
  • 改成 C:\Users\z7407\AppData\Local\Google\Chrome\User Data\Default

    • 等于用了两套配置 User Data 一套
    • Default 一套
    • 也可以将 chrome 默认数据改成 Default
      • --user-data-dir="C:\Users\z7407\AppData\Local\Google\Chrome\User Data\Default"
  • 下载一个测试版本的 chrome option.binary_location = r"D:\Downloads\chrome-win64\chrome.exe"

    • 下载地址
    • 下载后当新版本使用,会删除旧版本的 user datap
  • 通过腾讯电脑管家降级试试

  • 查看当前个人资料路径 chrome://version/

谷歌浏览器安装包无法打开

方案一

  • 卸载 chrome 浏览器,删除残留的文件(默认路径是:C:\Program Files (x86)\Google,把它全部删除),然后打开 ChromeSetup.exe 安装。

  • 组合键 win+R ,输入 regedit,打开注册表,进到 HKEY_CURRENT_USER\Software\Google\Chrome,并将其删掉,若您的电脑中沒有别的谷歌软件,请点此将全部 Google 删掉,然后就可以安装了。

方案二

  • 将 txt 文件改为 reg 文件后双击运行
Windows Registry Editor Version 5.00

; WARNING, this file will remove Google Chrome registry entries
; from your Windows Registry. Consider backing up your registry before
; using this file: http://support.microsoft.com/kb/322756

; To run this file, save it as 'remove.reg' on your desktop and double-click it.

[-HKEY_LOCAL_MACHINE\SOFTWARE\Classes\ChromeHTML]
[-HKEY_LOCAL_MACHINE\SOFTWARE\Clients\StartMenuInternet\chrome.exe]
[HKEY_LOCAL_MACHINE\SOFTWARE\RegisteredApplications]
"Chrome"=-

[-HKEY_CURRENT_USER\SOFTWARE\Classes\ChromeHTML]
[-HKEY_CURRENT_USER\SOFTWARE\Clients\StartMenuInternet\chrome.exe]
[HKEY_CURRENT_USER\SOFTWARE\RegisteredApplications]
"Chrome"=-

[-HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\Chrome]
[-HKEY_CURRENT_USER\Software\Google\]

[-HKEY_CURRENT_USER\Software\Google\]

[-HKEY_LOCAL_MACHINE\SOFTWARE\Google\]

[-HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Google\]

启动后没有报错

  • chrome driver {'status': [13, 'unknown error'], 'value': ''}

  • 将程序的 .env 里的代理关掉

  • 或者设置不走代理的部分 dns

HTTP_PROXY=http://192.168.31.2:9999
HTTPS_PROXY=http://192.168.31.2:9999
NO_PROXY=localhost,127.0.0.1,192.168.31.0/24
编程2 min read

  • 本文记录了为解决 Clash 订阅更新问题所进行的一系列尝试和最终实现的自动化方案。
  • 主要目标是克服订阅链接中 token 频繁变化带来的不便,并最终实现近乎全自动化的 Clash 订阅更新。

背景与痛点 🤔

  • 使用某机场提供的 Clash 订阅服务,该服务通过订阅链接提供配置信息。

  • 需要在手机、电脑和服务器上使用 Clash。

  • 订阅链接中的 token 会定期更新,每次都需要手动更新所有设备的配置,令人苦不堪言 😫。

  • 核心需求:

    • 实现多设备共享 Clash 配置。
    • 终极目标: 尽可能减少手动操作,实现全自动更新。

初始方案与架构

1. 局域网共享 (临时方案) 🔗

  • 最初,为了在手机上使用 Clash,利用电脑作为中转。
  • 电脑运行 Clash 后,手机通过局域网连接到电脑的 Clash 端口。
  • 再由电脑代理流量。
  • 问题: 电脑需要保持开机状态,关机了,手机也没网了。自动化程度:低

2. 引入服务器 🖥️

  • 为了更稳定地使用,在服务器上部署了 Clash for Linux。
  • 所有设备都可以通过系统代理连接到服务器上的 Clash,再由服务器连接到机场。
  • 小服务器 24 小时在线~
  • 问题:
    • 仍然需要手动更新所有设备的 Clash 配置。
    • 小服务定时脚本更新 Clash Docker,但无法应对订阅链接的变动。
    • 还需要手动重启 Docker。
    • 自动化程度:中(服务器 Clash 更新)。

解决 Token 动态变化:Cloudflare Worker 的尝试与转向 ☁️

  • 有了 Cloudflare 能解决本地网络访问不到的问题

  • 使用预设的固定 token 替换请求中的 token 参数,

    • 不妨碍其他小伙伴使用这个服务。
  • 并修改 Clash 配置文件的部分内容。

  • 在编写 Cloudflare Worker 脚本的时候就想过包括登录,

    • 但是碰到了 ja3 指纹的问题,
    • 所以放弃,这个东西用 Python 也很难解决。

Cloudflare Worker 的主要功能:

  • 接收客户端发出的订阅请求,避免客户端访问不到机场的问题。
  • 如果是特定的 key 请求,则将其替换为预设的 token,减少多端改动。
  • 修改从机场获取的 Clash 配置文件内容,例如:
    • 设置 interval180 秒,提高更新频率。
    • 添加 tolerance: 2000,增强更新的稳定性。
  • 伪装 User-Agent,避免订阅接口的拦截。
  • 将修改后的 Clash 配置文件返回给客户端。

Cloudflare Worker 脚本:

export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
var { pathname, search } = url;
if (search === "?token=KEYTOKEN") {
search = "?token=ASpecialToken";
}

const base_url = `https://bbb.aaa.com/api/v1/client/subscribe${search}`;
const modifiedHeaders = new Headers(request.headers);
modifiedHeaders.set('User-Agent', 'ClashForWindows/0.15.10');
const modifiedRequest = new Request(request, { headers: modifiedHeaders });

const image_response = await fetch(base_url, modifiedRequest);
const body = await image_response.text();
const modifiedYaml = body.replace(/(interval:)\s*\d+/g, '$1 180');
const modifiedYamlWithTolerance = body.replace(/(interval: 180)/g, '$1, tolerance: 2000');

return new Response(modifiedYamlWithTolerance, {
status: image_response.status,
statusText: image_response.statusText,
headers: image_response.headers,
});
},
};

优势:

  • 无需在客户端设备上直接配置机场订阅链接。
  • 只需更新 Worker 上的 token 即可同步所有设备的配置。
  • 隐藏了真实的订阅链接,提高了隐私性。

不足:

  • 仍然需要手动获取最新的有效 token 并更新到 Worker。
  • 还必须手动执行以下操作来完成整个更新流程:
    • 打开网页
    • 登录
    • 获取 token
    • 打开 Cloudflare 网站
    • ⭐ 等待 Cloudflare 加载
    • 更新 token
    • 打开 ssh 软件 tabby
    • 连接到 Linux 服务器。
    • 输入 运行脚本的命令
    • 输入 sudo 密码
    • 关闭 SSH 软件
  • 好麻烦 😫 自动化程度:低(Token 更新)。

自动化获取 Token:Selenium 的应用 ⚙️

  • 为了自动化获取最新的 token,
  • 编写了一个 Python 脚本,
  • 使用 Selenium 模拟浏览器登录机场并提取包含 token 的订阅链接。
  • 通过 Cloudflare API 更新 Worker 的环境变量
  • 最后通过 SSH 连接到服务器,重启 Docker 服务。
  • 能自动一点是一点 👍

遇到的小问题:

  • ChromeDriver 重启后需要重新下载,且下载慢

缓存 ChromeDriver 的 Python 代码:

def get_selenium_driver(headless=False):
driver_path = os.path.join(conf.SELENIUM_DRIVER_DOWNLOAD_DIR, "chromedriver.exe")
move_driver = False
if os.path.exists(driver_path):
service = Service(executable_path=driver_path)
else:
move_driver = True
service = None
user_cookies = os.path.join(
os.path.expanduser("~"), r"AppData\Local\Google\Chrome\User Data"
)
option = webdriver.ChromeOptions()
option.add_argument("--no-sandbox")
option.add_argument("--disable-dev-shm-usage")
option.add_argument("--user-data-dir={}".format(user_cookies))
if headless:
option.add_argument("--headless")

chrom_driver = webdriver.Chrome(options=option, service=service)
if move_driver:
shutil.copy2(chrom_driver.service.path, conf.SELENIUM_DRIVER_DOWNLOAD_DIR)
return chrom_driver

自动化更新 Cloudflare Worker Token 🔄

  • xpath 部分 略过
  • 获取到最新的 token 后,需要将其更新到 Cloudflare Worker 的环境变量中。

遇到的问题:

  • Cloudflare API 限制:
    • Cloudflare 的 HTTP API 无法直接更新 Worker 的环境变量。
    • 要用他们自己的工具 wrangler
    • wrangler 是 Cloudflare 提供的命令行工具,还需要 nodejs。
  • fnm 执行问题:
    • 使用 FNM 管理 Node.js 版本。
    • 但直接通过 Python 执行 fnm exec npx wrangler secret put 命令更新 token 失败。
    • 好像是环境变量问题。

解决方案:

  • 直接调用 Node.js 解释器来执行 wrangler 命令,从而更新 Worker 的环境变量。

Python 代码:

def update_cloudflare_worker_env(token):
api_token = os.environ["CLOUDFLARE_API_TOKEN"]
script_name = os.environ["script_name"]
env_name = os.environ["env_name"]
node_exe = os.environ["node_exe"] # where.exe node
wrangler_path = os.environ["wrangler_path"] # 安装 wrangler 后在当前目录下 找 wrangler.js
os.environ["CLOUDFLARE_API_TOKEN"] = api_token
command = f"echo {token} | {node_exe} {wrangler_path} secret put {env_name} --name {script_name}"
result = subprocess.run(command, shell=True, capture_output=True, text=True, encoding="utf-8")
print("Delete Output:", result.stdout)
if result.stderr:
print("Error:", result.stderr)

自动化更新 Clash 配置并重启服务 🛠️

  • 最后一步是在服务器上自动更新 Clash 的配置文件并重启 Clash 服务。编写了一个 update.sh 脚本来完成这些操作。

  • 遇到的问题:sudo 权限

  • 执行 update.sh 脚本需要 sudo 权限,使用 paramiko 库通过 SSH 连接到服务器并执行带 sudo 权限的命令。

Python 代码:

def run_restart_sh():
command = "sudo bash /home/root/server/update.sh "
l_password = os.environ["server_passwd"]
l_host = os.environ["server_ip"]
l_user = os.environ["server_username"]
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(l_host, username=l_user, password=l_password)
transport = ssh.get_transport()
session = transport.open_session()
session.set_combine_stderr(True)
session.get_pty()
session.exec_command(f"{command}")
stdin = session.makefile("wb", -1)
stdout = session.makefile("rb", -1)
stdin.write(l_password + "\n")
stdin.flush()

for line in stdout.read().decode("utf-8").splitlines():
print(f"host: {l_host}: {line}")
ssh.close()

总结:自动化升级,解放双手 🎉

  • 通过以上步骤,最终实现了一键运行,所有设备都能自动更新 Clash 订阅的操作。
  • 虽然当前的方案需要在电脑上运行脚本触发更新,但已经极大地减少了手动操作,提升了用户体验。
  • 要是有过 Ja3 和谷歌验证的方案。可以考虑将整个流程部署到服务器上,并设置定时任务。
编程7 min read


相关

  • 想法起源 别人的博客, 看起来很酷,还能显示当前人数。
  • 在 docusaurus 或者 markdown 中博客展示 google analysis 数据
  • 目前好像没有相关项目 可以做个类似 display card 的东西
  • 使用默认的 google analytics 查看数据
  • lookerstudio 可以调用 google analytics 并展示,但是自带的模板库没有使用最新的 API。
    • 报告地址
    • embed
    • 右上角分享 - 复制代码后 需要删除部分代码
    • 暂时不能实现的功能
      • 不能显示当前再看的人数
<!-- 代码参考 -->
<iframe width="800" height="600" src="https://lookerstudio.google.com/embed/reporting/6054b482-a054-42f9-870d-3fe15faeb78f/page/IpdTE">

编程One min read