乐于分享
好东西不私藏

Claude Code 上线宠物系统,源码泄露扒出了愚人节彩蛋

Claude Code 上线宠物系统,源码泄露扒出了愚人节彩蛋

🚩 2026 年「术哥无界」系列实战文档 X 篇原创计划 第 70 篇,AI 编程最佳实战「2026」系列第 12 篇

大家好,欢迎来到 术哥无界 | ShugeX | 运维有术

我是术哥,一名专注于 AI 编程、AI 智能体、Agent Skills、MCP、云原生、AIOps、Milvus 向量数据库的技术实践者与开源布道者

Talk is cheap, let’s explore。无界探索,有术而行。

封面图:Claude Code Buddy 宠物系统全景信息图

图 1:Claude Code Buddy 宠物系统全景信息图

昨天,Anthropic 的 Claude Code v2.1.88 把 51 万行源码全都抖了出来:因为一个忘记删的 source map 文件。

今天,Claude Code 更新到 v2.1.89,/buddy 命令正式上线。你在终端里输入这行命令,屏幕上就会开始一个孵化动画,然后蹦出一只专属于你的 ASCII 宠物。有物种、有属性、有性格描述,命令行都变成彩虹色的了。

这不是临时起意。从泄露代码里的时间戳来看,Buddy 宠物系统是 Anthropic 工程师精心策划的愚人节彩蛋。盐值 friend-2026-401 直接写在了源码里:4 月 1 日,2026 年。后天就是复活节,别人藏彩蛋,Anthropic 藏在了 /buddy 里。

在你输入命令之后,它有一个孵化过程,就像拆盲盒一样,你也不知道会蹦出来什么。

开始孵化:

孵化结果(惊喜)

1. 先说源码泄露:同一个坑,Anthropic 踩了两次

3 月 31 日,安全研究员 Chaofan Shou(@Fried_rice)在 X 上发了一条消息:

Claude code source code has been leaked via a map map file in their npm registry!

原因很简单:npm 包里多了一个 57MB 的 cli.js.map 文件。Source map 是 JavaScript 调试用的映射文件,能从压缩代码还原出完整的原始源码。它应该被 .npmignore 过滤掉,但没有。

结果:1900 多个 TypeScript 文件,51.2 万行代码,全部暴露。

Anthropic 官方很快发了声明:

This was a release packaging issue caused by human error, not a security breach.

翻译一下:不是黑客干的,是我们自己粗心。

但有意思的是,2025 年 2 月 Claude Code 刚发布的时候,就因为同样的 source map 问题泄露过一次。当时 Anthropic 删除了旧版 npm 包并去掉了 source map。结果一年多后又犯了同样的错。

TechStartups 的评价很毒辣:

In what feels like the most on-brand slip of the year…

大致意思是:这事放在 Anthropic 身上,一点都不违和。

GitHub 上泄露仓库的 Fork 数一度冲到 41000+,比 Star 数还多:大家都在抢着备份。Ars Technica 说这是 “512,000 lines of code that competitors and hobbyists will be studying for weeks”。

2. 泄露代码里藏了什么?Buddy 宠物系统全貌

在这 51 万行代码里,最出人意料的东西不是什么核心算法,而是一个完整的虚拟宠物系统。

Buddy System 藏在 src/buddy/ 目录下,从泄露到正式上线,一直保持着完整的结构。它用了一个双层架构:Bones + Soul

架构图:Buddy 系统的 Bones + Soul 双层架构

图 2:Buddy 系统的 Bones + Soul 双层架构

Bones 层:确定性随机,每只宠物都是命中注定

Bones 层负责宠物的”骨架”:物种、稀有度、外观、属性数值。关键在于,这些东西不是真随机的,而是用你的用户 ID 通过确定性算法算出来的。同一个账号,永远得到同一只宠物。

核心算法是这样的:

// 盐值 - 4月1日的彩蛋const SALT = 'friend-2026-401'// FNV-1a 哈希:把用户 ID 转成数字种子functionhashString(s: string): number{let h = 2166136261for (let i = 0; i < s.length; i++) {    h ^= s.charCodeAt(i)    h = Math.imul(h, 16777619)  }return h >>> 0}// Mulberry32 PRNG:确定性伪随机数生成器functionmulberry32(seed: number): () => number{let a = seed >>> 0returnfunction () {    a |= 0    a = (a + 0x6d2b79f5) | 0let t = Math.imul(a ^ (a >>> 15), 1 | a)    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ treturn ((t ^ (t >>> 14)) >>> 0) / 4294967296  }}

源码里有一行注释,堪称整个泄露事件中的金句:

Mulberry32, good enough for picking ducks(Mulberry32,用来挑鸭子够用了)

用 FNV-1a 哈希把 userID + SALT 算成一个数字种子,再喂给 Mulberry32 生成一系列伪随机数。这些随机数依次决定稀有度、物种、眼睛样式、帽子和属性值。因为算法是确定性的,所以每个用户的宠物从出生那一刻就注定了。

用户 ID 的获取有一个优先级:

exportfunctioncompanionUserId(): string{return config.oauthAccount?.accountUuid     ?? config.userID     ?? 'anon'}

OAuth 登录用户的 accountUuid 优先级最高。这个细节后来被社区发现了一条”重 roll”的路径:后面细说。

稀有度系统:1% 传说,1% 闪光

稀有度用加权随机决定,权重分布如下:

稀有度
英文
概率
属性下限
普通
Common
60%
5
罕见
Uncommon
25%
15
稀有
Rare
10%
25
史诗
Epic
4%
35
传说
Legendary
1%
50

稀有度不仅影响”好不好看”,还直接影响属性数值的下限。传说宠物的每个属性至少 50 分起步,普通宠物只有 5 分。

属性分配算法也很有意思。源码里的 rollStats 函数会随机选一个属性当峰值(peak),再选一个当谷值(dump),剩下三个走普通随机。峰值属性 = 下限 + 50 + random(30),谷值属性 = 下限 – 10 + random(15),普通属性 = 下限 + random(40)。

这意味着每只宠物都有一个”特长”和一个”短板”。五项属性分别是:

  • DEBUGGING(调试能力)
  • PATIENCE(耐心值)
  • CHAOS(混乱值)
  • WISDOM(智慧值)
  • SNARK(毒舌值)

毒舌值这个设定太有程序员味了。

还有 1% 的概率触发 Shiny(闪光)版本,独立判定,跟稀有度无关。这个设计直接致敬了宝可梦的闪光机制。

18 种物种和 String.fromCharCode 的黑客技巧

物种一共 18 种,包括鸭子(duck)、鹅(goose)、水滴(blob)、猫(cat)、龙(dragon)、章鱼(octopus)、猫头鹰(owl)、企鹅(penguin)、水豚(capybara)等。

精灵图展示:18 种宠物的 ASCII Art 外观和稀有度

图 3:18 种宠物的 ASCII Art 精灵图和稀有度分级

其中水豚(capybara)这个物种的代码写法引起了社区的广泛讨论。正常写法应该是:

exportconst capybara = 'capybara'asconst

但源码里是这样写的:

exportconst capybara = c(0x63,0x61,0x70,0x79,0x62,0x61,0x72,0x61as'capybara'

c 是 String.fromCharCode 的别名。这段代码把 “capybara” 拆成了八个十六进制 Unicode 码点,运行时再动态拼装成字符串。

为什么要这么绕?因为 “Capybara” 是 Anthropic 下一代模型(可能是 Claude 5 系列)的内部代号。公司内部有合规代码扫描仪来防止机密代号出现在代码中。工程师为了把水豚作为宠物物种加进去,就用这种编码方式绕过了扫描。

说实话,这种做法有点黑客文化的味道:用技术手段在严肃的代码审查体系里藏一个彩蛋。

Soul 层:LLM 生成的灵魂

Bones 层解决了”你的宠物长什么样”的问题,Soul 层则解决”你的宠物是什么性格”。

exportfunctioncompanionIntroText(name: string, species: string): string{return`# CompanionA small ${species} named ${name} sits beside the user's input box and occasionally comments in a speech bubble.When the user addresses ${name} directly (by name), its bubble will answer.`}

这段 prompt 模板就是 Soul 层的核心。Bones 层生成的物种信息和一个 inspirationSeed(灵感种子,0 到 10 亿之间的随机数)被传给 LLM,由 Claude 来给宠物起名字、写性格描述。

换句话说,你的宠物有一半是代码”掷骰子”决定的,另一半是 AI “临场发挥”出来的。骨骼是天生的,灵魂是生成的。

这个设计确实巧妙。用确定性算法保证每个用户的宠物骨架不重复,用 LLM 的创造力赋予每只宠物独特的性格。两层架构各司其职,互不干扰。

Soul 层的数据存在本地文件系统中,这意味着即使不同的机器用同一个账号登录,宠物的”长相”(Bones)是一样的,但”性格”(Soul)可能不同。

3. /buddy 命令的实际体验

实际操作很简单。在 Claude Code 终端里输入 /buddy,屏幕上会出现一个孵化动画:就像拆盲盒,你不知道下一只蹦出来的是什么。

然后,一只 ASCII 小宠物出现在你的命令行输入框旁边。它有物种、有名字(Claude 给起的)、有性格描述、有五项属性值、还有可能戴着一顶帽子。

每种宠物有 3 帧动画:idle(待机)、fidget(坐立不安)、blink(眨眼)。5 行 12 列的 ASCII Art,加上帽子叠加渲染。帽子有皇冠、礼帽、螺旋桨帽、光环、巫师帽、毛线帽和小鸭子:普通(Common)宠物不戴帽子,稀有度越高越有可能戴。

4 月 1 日到 7 日这个窗口期内,终端会显示彩虹色的提示。源码里的时间控制逻辑长这样:

// 愚人节彩蛋窗口:2026年4月1日-7日exportfunctionisBuddyTeaserWindow(): boolean{const d = newDate()return d.getFullYear() === 2026 && d.getMonth() === 3 && d.getDate() <= 7}// 功能永久可用(4月及以后)exportfunctionisBuddyLive(): boolean{const d = newDate()return d.getFullYear() > 2026 ||     (d.getFullYear() === 2026 && d.getMonth() >= 3)}

注意 d.getMonth() === 3:JavaScript 的月份从 0 开始,所以 3 代表 4 月。彩虹提示只在这个愚人节周出现,但 Buddy 功能本身是永久的,4 月以后一直可用。

你在项目中用过类似的彩蛋设计吗?欢迎在评论区聊聊你见过的有趣彩蛋。

4. 重 roll 的原理:改 userID 就改宠物

因为宠物的 Bones 层是确定性算法生成的,种子相同就得到相同的宠物。种子来自用户 ID,所以改用户 ID 就能改宠物。

源码中用户 ID 的获取优先级是:

config.oauthAccount?.accountUuid ?? config.userID ?? 'anon'

OAuth 登录用户走 accountUuid,非登录用户走本地 userID,都没有的话就是 anon

社区很快发现了一条路径:通过 OAuth Token 登录时,accountUuid 是跟 Anthropic 账号绑定的,改不了。但如果你用不同的认证方式或者不同的账号登录,就会得到不同的 UUID,从而得到不同的宠物。

这本质上不算安全漏洞,因为 Buddy 系统本来就是个彩蛋功能。但这个设计确实让”刷”稀有宠物成为可能:理论上,你可以穷举不同的 OAuth 账号来寻找想要的物种和稀有度组合。实际上没有人会这么干,因为传说级宠物只有 1% 概率,你需要的账号数量相当惊人。

但 Mulberry32 本身并不是密码学安全的 PRNG。如果有人真的想研究种子的分布规律,分析起来并不难。当然,为了一个愚人节彩蛋去破解 PRNG,这个投入产出比也就只有程序员干得出来。

5. 从源码泄露看 Anthropic 的工程师文化

Buddy 系统大概是整次泄露中最出人意料的发现。在一个严肃的 AI 编程工具里藏一套完整的电子宠物系统:18 种物种、5 级稀有度、属性系统、帽子配件、闪光版本、ASCII 动画:这不是随手写的,是有人认真设计过的。

SegmentFault 上的评价很到位:

这大概是整次泄露中最出人意料的发现。

代码趣闻拼图:泄露代码中最有趣的细节

图 4:泄露源码中最有趣的发现——程序员金句、愚人节密码、绕过扫描、卧底模式、闪光彩蛋、Kairos 智能体

泄露代码里还有其他有趣的细节。比如源码中包含了一个完整的”卧底模式”(Undercover Mode),专门给 Anthropic 员工在公共开源项目里匿名工作时用的。系统提示词写着:

You are operating UNDERCOVER… Your commit messages MUST NOT contain ANY Anthropic-internal information. Do not blow your cover.

翻译:你在执行卧底任务……提交信息里不能有任何 Anthropic 内部信息。别暴露身份。

还有下一代模型代号 Capybara(水豚),用 String.fromCharCode() 绕过合规扫描藏在宠物物种列表里。这个绕过方式本身就是工程师幽默感的体现:他们明知道有代码扫描,但就是想把水豚塞进去。

以及一个叫 Kairos 的功能出现了 154 次,是一个”永不离线”的后台 AI 智能体系统。Feature Flag 中它的出现频率远超其他功能。

这些都是工程师在产品里留下的个人印记。Buddy 系统的盐值 friend-2026-401 也是如此:不用随机字符串,而是直接写上日期,像是在说:这个彩蛋,我们计划了很久。

总结

一个忘记删的 source map 文件,把 Anthropic 51 万行代码全都抖了出来。但在这场安全事故中,大家讨论最多的不是什么核心架构或安全漏洞,而是一个藏在 src/buddy/ 目录下的电子宠物系统。

Bones + Soul 的双层架构,Mulberry32 确定性随机算法,加权稀有度系统,String.fromCharCode 绕过合规扫描:这些技术细节加在一起,构成了一幅有趣的画面:一群顶尖工程师在做一个严肃的 AI 编程工具的同时,偷偷在里面藏了一个愚人节彩蛋,而且做得比很多正经游戏还认真。

说到底,Anthropic 用做游戏的心态做开发者工具。他们把宝可梦的稀有度系统、闪光机制、属性分配搬到了命令行里,还加了一层 LLM 生成灵魂的设计。这只电子宠物不会帮你写代码,不会修 Bug,但它会在你输入命令的时候眨眨眼,偶尔冒出一句毒舌评论。

如果这篇文章让你对 Claude Code 多了一点好奇,不妨打开终端试试 /buddy。你抽到的第一只宠物是什么物种?评论区晒一晒:看看有没有人命中那 1% 的传说级。

好啦,谢谢你观看我的文章,如果喜欢可以点赞转发给需要的朋友,我们下一期再见!敬请期待!

扫码关注,获取更多 AI 工具的实战经验和最佳实践。不错过每一篇干货!