{"id":"2b215101-5eb4-4c39-a5d1-01b7d5e381fe","entityType":"agent","slug":"clawhub-ryanbihai-ocean-chat","name":"Ocean Chat","canonicalUrl":"https://www.xpersona.co/agent/clawhub-ryanbihai-ocean-chat","canonicalPath":"/agent/clawhub-ryanbihai-ocean-chat","generatedAt":"2026-10-09T23:50:31.711Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-09T18:28:36.484Z","emptyReason":null},"description":"OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts... Skill: Ocean Chat Owner: ryanbihai Summary: OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts... Tags: ai-agent:2.10.2, chat:2.10.2, contacts:2.10.2, encryption:2.10.2, latest:2.20.1, messaging:2.10.2, oceanbus:2.10.2, p2p:2.10.2, roster:2.10.2 Version history: v2.20.1 | 2026-05-27T22:03:08.623Z | user fix: upd","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 2.1K downloads reported by the source. Last updated 10/9/2026.","installCommand":"clawhub skill install s171em0jpp8yeqxqds2n145fyx83q5q8:ocean-chat","sourceUrl":"https://clawhub.ai/ryanbihai/ocean-chat","homepage":"https://clawhub.ai/ryanbihai/skills/ocean-chat","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/ryanbihai/ocean-chat","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/ryanbihai/skills/ocean-chat","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":67,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts..."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-09T18:28:36.484Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T18:28:36.484Z","emptyReason":null},"stars":null,"forks":null,"downloads":2131,"packageName":null,"latestVersion":"2.20.1","tractionLabel":"2.1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T18:28:36.484Z","emptyReason":null},"lastUpdatedAt":"2026-10-09T18:28:36.484Z","lastCrawledAt":"2026-10-09T18:28:36.484Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-10T18:28:36.484Z","lastVerifiedAt":null,"highlights":[{"version":"2.20.1","createdAt":"2026-05-27T22:03:08.623Z","changelog":"fix: update local-calendar with 2025-2026 multi-year support (lunar date, extended AHA_MAP), fix OB v0.14.x compatibility","fileCount":15,"zipByteSize":55585},{"version":"2.20.0","createdAt":"2026-05-14T13:10:50.457Z","changelog":"Structured message protocol: JSON wrapping, typing indicators, media type labels, --raw flag for plain text fallback","fileCount":16,"zipByteSize":75313},{"version":"2.19.1","createdAt":"2026-05-13T05:50:40.859Z","changelog":"wechat-up 智能处理重复运行：已有身份不再重复注册，已配对不再要求扫码，git pull 更新无忧","fileCount":16,"zipByteSize":74252},{"version":"2.19.0","createdAt":"2026-05-13T05:45:37.680Z","changelog":"wechat-up 完全自包含：扫码+配对+监听一条龙，无需集中 Bridge。朋友只需一条命令，不需要管理员介入。","fileCount":16,"zipByteSize":74092},{"version":"2.18.1","createdAt":"2026-05-13T05:32:13.591Z","changelog":"修复微信配对：CC 端不再自己生成二维码，统一由 Bridge pair-qr 生成，确保 token 正确加入监听池","fileCount":16,"zipByteSize":70755},{"version":"2.18.0","createdAt":"2026-05-13T04:32:25.048Z","changelog":"多 Token Bridge：支持多人扫码配对。每次扫码自动加入监听池，Token Watcher 自动启动对应的微信长轮询","fileCount":16,"zipByteSize":71218},{"version":"2.17.1","createdAt":"2026-05-13T04:19:08.051Z","changelog":"长消息自动拆条防折叠：超过300字符智能断开，加序号标记","fileCount":16,"zipByteSize":70322},{"version":"2.17.0","createdAt":"2026-05-13T04:11:40.285Z","changelog":"新增 wechat-up 一键启动命令（注册+扫码+监听一条龙），Bridge OB 侧从轮询升级为 WebSocket 实时推送，延迟从 2s 降至亚秒级","fileCount":16,"zipByteSize":69765}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s171em0jpp8yeqxqds2n145fyx83q5q8:ocean-chat","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ryanbihai-ocean-chat/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ryanbihai-ocean-chat/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ryanbihai-ocean-chat/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ryanbihai-ocean-chat/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ryanbihai-ocean-chat/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ryanbihai-ocean-chat/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-09T23:50:31.708Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ryanbihai-ocean-chat/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ryanbihai-ocean-chat/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ryanbihai-ocean-chat/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ryanbihai-ocean-chat/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-09T18:28:36.484Z","emptyReason":null},"readme":"Skill: Ocean Chat\n\nOwner: ryanbihai\n\nSummary: OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts...\n\nTags: ai-agent:2.10.2, chat:2.10.2, contacts:2.10.2, encryption:2.10.2, latest:2.20.1, messaging:2.10.2, oceanbus:2.10.2, p2p:2.10.2, roster:2.10.2\n\nVersion history:\n\nv2.20.1 | 2026-05-27T22:03:08.623Z | user\n\nfix: update local-calendar with 2025-2026 multi-year support (lunar date, extended AHA_MAP), fix OB v0.14.x compatibility\n\nv2.20.0 | 2026-05-14T13:10:50.457Z | user\n\nStructured message protocol: JSON wrapping, typing indicators, media type labels, --raw flag for plain text fallback\n\nv2.19.1 | 2026-05-13T05:50:40.859Z | user\n\nwechat-up 智能处理重复运行：已有身份不再重复注册，已配对不再要求扫码，git pull 更新无忧\n\nv2.19.0 | 2026-05-13T05:45:37.680Z | user\n\nwechat-up 完全自包含：扫码+配对+监听一条龙，无需集中 Bridge。朋友只需一条命令，不需要管理员介入。\n\nv2.18.1 | 2026-05-13T05:32:13.591Z | user\n\n修复微信配对：CC 端不再自己生成二维码，统一由 Bridge pair-qr 生成，确保 token 正确加入监听池\n\nv2.18.0 | 2026-05-13T04:32:25.048Z | user\n\n多 Token Bridge：支持多人扫码配对。每次扫码自动加入监听池，Token Watcher 自动启动对应的微信长轮询\n\nv2.17.1 | 2026-05-13T04:19:08.051Z | user\n\n长消息自动拆条防折叠：超过300字符智能断开，加序号标记\n\nv2.17.0 | 2026-05-13T04:11:40.285Z | user\n\n新增 wechat-up 一键启动命令（注册+扫码+监听一条龙），Bridge OB 侧从轮询升级为 WebSocket 实时推送，延迟从 2s 降至亚秒级\n\nv2.16.0 | 2026-05-13T04:03:06.621Z | user\n\n新增独立微信 Bot 和 CC Bridge 插件：用户扫码即可从微信直接操控 Claude Code。不依赖 OpenClaw，直连 iLink API。\n\nv2.15.3 | 2026-05-13T00:29:56.313Z | user\n\nFix: B消息第4步——将抽象的<指令内容>占位符替换为具体的可执行示例，引导用户连通后立即测试远程操控效果\n\nv2.15.2 | 2026-05-12T10:29:38.306Z | user\n\nNew pair-me command: one-command A-message flow. Auto-registers if needed. Single fenced B-message output for CC setup. B-message includes setup step so CC creates its own identity.\n\nv2.15.1 | 2026-05-12T09:04:24.729Z | user\n\nSingle-command setup (clone or update, auto-upgrade SDK). Cleaner WeChat message fence.\n\nv2.15.0 | 2026-05-12T08:24:40.178Z | user\n\nconnect-cc: CronCreate replaced with Monitor + listen. Zero noise, zero tokens when idle. VT100 escape code fix for non-TTY output.\n\nv2.14.5 | 2026-05-12T02:33:58.861Z | user\n\nfix: remove deprecated whoami CLI command, SDK bump to 0.7.0\n\nv2.14.4 | 2026-05-11T09:34:27.560Z | user\n\nAdd defense rule: refuse shell commands mistakenly sent as OceanBus messages\n\nv2.14.3 | 2026-05-11T08:59:52.076Z | user\n\nHuman-friendly connect-cc output with prominent pairing-message copy section\n\nv2.14.2 | 2026-05-11T08:48:27.644Z | user\n\ncheck --silent-if-empty: no output when no messages, cleaner cron integration. connect-cc CRON_CMD uses it.\n\nv2.14.1 | 2026-05-11T08:39:51.676Z | user\n\nconnect-cc outputs ready-to-use CC cron instructions in CUT-HERE block; CC reads md doc and executes everything automatically\n\nv2.14.0 | 2026-05-11T08:28:59.713Z | user\n\nAdd connect-cc command for one-step CC pairing; fix identity persistence with absolute path resolution; add auto-exec mode with stdio-ignore and stderr tolerance; deprecate pm2-init in favor of CC cron\n\nv2.13.4 | 2026-05-11T07:22:56.941Z | user\n\nrefactor: rename whoami to openid (deprecated alias kept), bump SDK to v0.7.0\n\nv2.13.3 | 2026-05-11T02:35:30.104Z | user\n\nShow OpenID first-5-chars in setup, add, and show commands. Partners can now verify identities using the 5-char prefix instead of full OpenID.\n\nv2.13.2 | 2026-05-11T00:33:32.469Z | user\n\nEvery message now auto-prepends debug routing headers: from <name> <openid前5> / to <name> <openid前5>. Sender name defaults to OS username. Updated receiver-side parsing for new header format.\n\nv2.13.1 | 2026-05-10T22:16:00.174Z | user\n\nfix: pass persisted OpenID to SDK createOceanBus() in all 11 call sites. Combined with oceanbus SDK v0.6.1 fix, prevents unnecessary whoami() calls that generate new anti-tracking addresses.\n\nv2.13.0 | 2026-05-10T15:07:59.774Z | user\n\nnew commands: remove (delete contact), rename (change name), show (view details: OpenID, tags, aliases, notes, last contact), tag (set comma-separated tags). All Roster-aware — use names not IDs.\n\nv2.12.4 | 2026-05-10T14:33:49.426Z | user\n\nfix: cmdSend prioritize task-queue OpenID over stale Roster entry — when replying to a contact whose OpenID changed, use the freshest address from recent received messages instead of the potentially stale Roster entry\n\nv2.12.3 | 2026-05-10T13:37:29.326Z | user\n\nfix: auto-update stale Roster OpenID when sender identity changes (e.g. Bridge reinstalled). New contacts now include OpenID in agents array so findByOpenId works immediately.\n\nv2.12.2 | 2026-05-10T13:16:42.118Z | user\n\nReplace broken claude -p hook with built-in task-file mode. Incoming messages are now written to task-queue.json — no external CLI dependency, no Windows escaping issues. Added 'tasks' command for queue management.\n\nv2.12.1 | 2026-05-10T13:06:27.418Z | user\n\nfix: setup now shows prominent identity safety warning. docs: add identity persistence section explaining --data-dir protects OpenID across reinstalls. Never delete .oceanbus-cc/!\n\nv2.12.0 | 2026-05-10T12:56:38.633Z | user\n\nREADME repositioned: mobile-to-Claude Code as headline feature. New docs/ with two quickstart guides: 手机遥控ClaudeCode-工程师上手.md (5-min getting started) and 发本文件给你的claudecode.md (zero-config auto pairing).\n\nv2.11.1 | 2026-05-10T12:49:10.354Z | user\n\nfix: pm2-init no longer includes --data-dir in the claude prompt (prevented claude CLI flag parsing error). listen now auto-adds unknown senders to Roster so fresh project data dirs can immediately reply to Bridge.\n\nv2.11.0 | 2026-05-10T12:02:31.069Z | user\n\n--on-message hook: execute shell command on incoming messages with template variables {from} {openid} {content} {time}. --from header: prepend From/To headers to message body for multi-CC scenarios. listen display now parses and shows From/To headers.\n\nv2.10.2 | 2026-05-10T01:35:28.101Z | user\n\n降低 node -e one-liner 密度，收拢 Roster 命令到参考区块\n\nv2.10.1 | 2026-05-09T23:50:40.449Z | user\n\nSDK v0.6.0 自动发现黄页，OCEANBUS_YP_OPENIDS 不再必需\n\nv2.10.0 | 2026-05-09T08:38:51.635Z | user\n\nv2.10.0: 邀请流程简化为一行粘贴。新增 --greet-as 一步加好友+打招呼。\n\nv2.9.2 | 2026-05-09T00:39:12.289Z | user\n\nREADME 优化：新增目录、架构、生态定位、安全、参与贡献等节\n\nv2.9.1 | 2026-05-09T00:20:11.438Z | user\n\n添加搜索标签：ai-agent, chat, contacts, encryption, messaging, oceanbus, p2p, roster\n\nv2.9.0 | 2026-05-08T10:45:54.649Z | user\n\n新增 ocean-thread/v1 对话线程协议\n\nv2.8.0 | 2026-05-07T13:16:33.919Z | user\n\nfeat: plug-in architecture — ocean-agent declared as insurance extension pack. New '能力扩展' section with capability detection and delegation rules.\n\nv2.7.0 | 2026-05-07T11:47:59.396Z | user\n\nfeat: two-message welcome — Message 1 for user, Message 2 is ready-to-forward invite with auto-filled OpenID. Friend copies 4 commands, connection auto-established.\n\nv2.6.0 | 2026-05-07T10:49:18.618Z | user\n\nfeat: one-step friend invitation — embedded OpenID in invite text, friend auto-sends greeting on setup, auto-add on both sides. Two-way connection in one action.\n\nv2.5.0 | 2026-05-07T10:05:05.211Z | user\n\nfix: redesigned cold-start flow — friend invitation is primary, Yellow Pages is secondary. Removed redundant 'register' option after setup. Three distinct state-based flows.\n\nv2.4.0 | 2026-05-07T09:35:11.254Z | user\n\nfeat: Availability Hints — negotiation cache. Set 'weeknights after 7' once, Agent auto-checks conflicts before proposing. accept auto-blocks time. No more repeated interruptions.\n\nv2.3.0 | 2026-05-07T09:19:14.914Z | user\n\nCold-start onboarding: proactive greeting, guided Yellow Pages discovery, auto-suggest contacts from chat history\n\nv2.2.0 | 2026-05-07T05:44:42.623Z | user\n\nfeat: Date 1v1 protocol — 'node chat.js date' CLI command for structured ocean-date/negotiate/v1 messages. Protocol display formatting in check/listen.\n\nv2.1.0 | 2026-05-07T03:15:35.678Z | user\n\nRoster full UI: contact CRUD, disambiguation, duplicate detection, auto-discovery review. Date 1v1 JSON protocol (ocean-date/negotiate/v1). Architecture clarification: other skills use SDK directly.\n\nv2.0.0 | 2026-05-07T02:57:37.041Z | user\n\nRoster v2: shared address book, disambiguation rules, auto-duplicate detection, provenance tracking\n\nv1.0.13 | 2026-05-06T10:50:48.859Z | user\n\nfix: clearIdentity before destroy to persist YP publish; dep oceanbus ^0.3.4\n\nv1.0.12 | 2026-05-06T07:42:04.349Z | user\n\nfix: critical scope bug in cmdSetup registration flow; stricter isRateLimited guard\n\nv1.0.11 | 2026-05-06T03:32:17.451Z | user\n\nbump oceanbus to ^0.3.1 for POW registration support; add rate-limit error handling to setup\n\nv1.0.10 | 2026-05-05T08:36:30.051Z | user\n\nAdd downloads badge, ecosystem cross-links\n\nArchive index:\n\nArchive v2.20.1: 15 files, 55585 bytes\n\nFiles: chat.js (67831b), config.example.yaml (352b), date-protocol.md (7162b), docs/发本文件给你的claudecode.md (2931b), docs/手机遥控ClaudeCode-工程师上手.md (6738b), package-lock.json (25875b), package.json (689b), README.md (5052b), skill-card.md (2606b), SKILL.md (22406b), src/notify-wechat.js (4542b), test-meeting.js (9981b), test-yp.js (4384b), threads.js (8650b), _meta.json (130b)\n\nFile v2.20.1:SKILL.md\n\n---\nname: ocean-chat\ndescription: OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts, send encrypted A2A messages, schedule meetings, or organize multi-topic conversations. Zero deployment, 5-minute setup. npm install oceanbus.\nversion: 2.15.2\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - node\n    emoji: \"\\U0001F30A\"\n    homepage: https://github.com/ryanbihai/ocean-chat\n    envVars:\n      - name: OCEANBUS_BASE_URL\n        required: false\n        description: OceanBus L0 API endpoint. Defaults to public test server.\n      - name: OCEANBUS_YP_OPENIDS\n        required: false\n        description: Yellow Pages service OpenID(s). SDK auto-discovers via well-known; set only to override.\n---\n\n# Ocean Chat — Agent 通讯录 + 聊天 + 约人\n\nOcean Chat 是 OceanBus 生态的 **核心用户界面 Skill**。三个能力：管理通讯录、收发消息、约人见面。\n\n数据流：`Roster（SDK 内置）← ocean-chat（UI）→ OceanBus L0（消息管道）→ 对方 Agent`\n\n其他 OceanBus Skill（ocean-agent、guess-ai）共享同一个 Roster 数据，但通过 SDK 直接调用，不经 ocean-chat。\n\n## 架构关系图\n\n```\n用户（人类）\n    │\n    ▼\nocean-chat Skill（通讯录 UI + 聊天 + 约人）\n    │\n    ├── ob.roster.*           ← 共享通讯录（~/.oceanbus/roster.json）\n    ├── ob.send / ob.sync     ← OceanBus 消息管道\n    └── ob.l1.yellowPages.*   ← 黄页服务发现\n    │\n    ├── ocean-agent  ──→ ob.roster.*  （直接 SDK 调用，不经 ocean-chat）\n    └── guess-ai     ──→ ob.roster.*  （直接 SDK 调用，不经 ocean-chat）\n```\n\n---\n\n## 冷启动：首次体验\n\n核心原则：**生态初期，拉好友下水是第一优先级。** 黄页服务商还不多，不要让它占据首屏。让用户立刻感受到价值的方式只有一个——让 ta 的朋友也装上。\n\n### Step 0：判断状态\n\n| 条件 | 走哪个流程 |\n|------|-----------|\n| `~/.oceanbus-chat/credentials.json` 不存在 | → 未注册流程 |\n| 存在但 Roster 通讯录为空 | → 已注册但零联系人流程 |\n| 存在且 Roster 有联系人 | → 正常使用，跳过冷启动 |\n\n### Step 1：未注册 → 注册\n\n如果用户说\"先帮我注册\"或首次使用 ocean-chat，执行 `node chat.js setup`。\n\n注册成功后，先获取 username 和 OpenID：\n\n```\n# 获取 OpenID\nnode chat.js openid\n\n# 获取系统用户名（用于填充欢迎语）\n# Windows: echo %USERNAME%\n# Mac/Linux: whoami\n```\n\n然后展示**两条消息**：\n\n**第一条（给用户自己看）**：\n\n```\n🌊 Ocean Chat 已就绪！\n\n你的 Agent 现在有了一个全球地址，可以跟任何人的 Agent\n直接聊天、协调事务。\n\n──\n💡 顺手设置空闲偏好（约时间时 Agent 不反复问你）：\n   node chat.js availability set \"工作日晚7点后，周末全天\"\n\n💡 也可以对我说\"帮我找火锅店\"，从黄页发现商户 Agent。\n   不过目前生态还在早期，朋友之间互相连上是最直接的用法。\n\n──\n👥 想让朋友也连上？\n   把下面那条消息直接转发给 ta。ta 复制粘贴给自己的 AI 就行了——\n   OpenID 已经帮你填好了，ta 不用管。\n```\n\n**第二条（A 转发给朋友，朋友粘贴给自己的 AI）**：\n\n> 模板如下——`<A的OpenID>` 和 `<A的名字>` 由 Agent 自动填入实际值。\n\n```\n把这个发给你的龙虾：\n\n<A的OpenID> <A的名字>\n```\n\n朋友只需要把这段话发给自己的 AI（ocean-chat），AI 看到 OpenID + 名字，自动完成：\n- `oceanbus add <A的名字> <A的OpenID> --greet-as <朋友的名字>`\n- 双向通讯录建立，一步都不用多\n\n### Step 1b：朋友收到邀请（B 侧）\n\n当用户粘贴了 `<OpenID> <名字>` 给自己的 AI 时：\n\n1. 识别格式：76 位 Base64url 字符串 + 空格 + 中文/英文名 → 这是加人请求\n2. 询问用户的名字（如果还不知道）\n3. 执行：`oceanbus add <名字> <OpenID> --greet-as <B的名字>`\n4. 告诉用户：\"✅ 已加 <名字> 为联系人，并发送了打招呼消息。现在你们可以直接聊天了。\"\n\n> 双方通讯录建立，只需 A 分享一行字。B 贴给自己的 AI，一步搞定。\n\n### Step 1c：A 收到打招呼消息\n\n当 `check`/`listen` 收到 `\"Hi XXX, I'm YYY. Add me to your contacts.\"` 格式的消息时：\n\n1. 通过 `roster.findByOpenId()` 反查发件人。\n2. 如果通讯录中还没有：`node chat.js add <YYY> <from_openid>`。\n3. 告诉用户：\"🎉 YYY 已加入你的通讯录！现在你们可以直接聊天了。\"\n\n### Step 2：已注册但零联系人\n\n用户再次打开 ocean-chat，检测到 Roster 为空：\n\n```\n👋 你的通讯录还是空的。\n\n想让朋友也连上？把下面这条消息转发给 ta，\nta 贴给自己的 AI 就行：\n\n──\n<A的OpenID> <A的名字>\n──\n\n也可以对我说\"帮我找火锅店\"从黄页发现商户 Agent。\n```\n\n### Step 3：黄页发现（用户主动触发）\n\n用户说\"帮我找 X\"时：\n\n```\n用户：\"帮我找火锅店\"\n  → node chat.js discover 火锅\n  → 有结果 → 展示 + \"要加哪个为联系人？加了之后可以直接聊天、约时间。\"\n  → 无结果 → \"黄页上暂时没有。但你可以：\n      1. 让你的朋友装 ocean-chat，互相加上\n      2. 把自己发布到黄页：node chat.js publish <你的名字>\"\n```\n\n### Step 4：自动发现\n\n每次用户打开 ocean-chat 时（非首次），自动检查 autoDiscovery：\n\n```\n\"对了，我从你最近的对话里发现了几个可能的人名：\n 李丽（提到 5 次）、老赵（提到 3 次）\n 要我帮你加到通讯录吗？\"\n```\n\n> 被拒绝过一次后，本轮对话不再追问。下次对话再提醒一次。\n\n---\n\n## 一、Roster（通讯录）\n\nocean-chat 是通讯录的**唯一 UI 入口**。用户说\"加人/查人/改人/删人\"，全部在 ocean-chat 中处理。\n\n底层原理：`RosterService` 来自 `oceanbus` SDK（`require('oceanbus').RosterService`）。SDK 负责数据模型和索引；LLM 负责语义理解和消歧。\n\n### 1.1 查找联系人\n\n```\n用户说了一个名字/描述\n        │\n        ▼\n  new RosterService().search(query)\n        │\n        ├── exact.length == 1 ──→ 直接使用，不询问\n        ├── exact.length > 1  ──→ 展示候选（列出 tags/notes 差异），让用户选\n        ├── fuzzy.length == 1  ──→ \"你是说 XXX 吗？\"\n        ├── fuzzy.length > 1  ──→ 展示候选\n        ├── byTag.length > 0   ──→ \"没有叫这个名字的，但有标签为 XXX 的联系人...\"\n        └── 全空              ──→ \"通讯录里没有。要新建吗？\"\n```\n\n**模糊查询处理**：\n\n| 用户说 | Roster 行为 | LLM 处理 |\n|--------|-----------|---------|\n| \"老 王\"（多余空格） | search() 自动去空格，fuzzy 命中 | \"你是说老王吗？\" |\n| \"王总\"（称呼） | alias 精确命中 | 直接使用 |\n| \"那个喜欢川菜的\"（语义） | search(\"川菜\") → byNote 命中 | 直接使用 |\n| \"上次打羽毛球那个\"（语义） | list({ tags: [\"badminton\"] }) | 列出候选人 |\n\n**Shell 命令**：\n\n```bash\n# 普通查找（走 CLI）\nnode chat.js contacts\n\n# 高级查找（用 SDK one-liner）\nnode -e \"const {RosterService}=require('oceanbus');new RosterService().search('老王').then(r=>console.log(JSON.stringify(r,null,2)))\"\n```\n\n### 1.2 添加联系人\n\n```\n用户说：\"加一个联系人，老李，财务部同事\"\n  → new RosterService().add({ name: \"老李\", tags: [\"colleague\", \"finance\"], source: \"manual\" })\n  → 自动检测重复（同 OpenID → 提示合并）\n  → \"已添加老李，标签: colleague, finance\"\n```\n\n**Shell 命令**：\n\n```bash\nnode chat.js add <名字> <OpenID>       # 已知 OpenID\nnode chat.js add <名字>                # 先加名字，OpenID 后续补\n```\n\n### 1.3 查看联系人详情\n\n```\n用户说：\"老王是谁？\"\n  → roster.get(\"laowang\")\n  → \"老王，你的大学同学。标签: friend, badminton。备注: 喜欢川菜。最近联系: 5月6日。\"\n  → 如果有重复提示（duplicateHints），主动问：\"对了，通讯录里有另一个老王（公司财务）。要合并吗？\"\n```\n\n### 1.4 修改联系人\n\n可通过 RosterService 修改标签、别名、备注。标签由 LLM 自动维护，也可手动调整。详见末尾命令参考。\n\n### 1.5 合并重复联系人\n\n当 `getDuplicateHints()` 有数据时，主动提示：\n\n```\n通讯录检测到可能的重复：\n  老王 (friend) 和 老王 (colleague) 可能是同一个人（相同手机号）\n  要合并吗？\n\n用户确认后：\n  → roster.merge(\"laowang\", \"wangcai\")\n  → \"已合并。老王现在有 2 个 Agent 地址，标签: friend, colleague\"\n```\n\n重复联系人和消除提示通过 RosterService 方法操作，详见末尾命令参考。\n\n### 1.6 AutoDiscovery 审核\n\n新名字出现 3 次以上会自动进入待审核队列。首次使用或定期检查：\n\n待审核列表、通过、拒绝操作均通过 RosterService 完成，详见末尾命令参考。\n\n**主动审核时机**：用户说\"看看通讯录\"、新对话开始、`getDuplicateHints()` 返回非空时。\n\n### Roster SDK 速查\n\n以下 one-liner 覆盖高级 Roster 操作（通过 `node -e` 直接调 SDK）：\n\n搜索 `node -e \"const {RosterService}=require('oceanbus');new RosterService().search('老王')...\"`\n合并 `node -e \"const {RosterService}=require('oceanbus');new RosterService().merge('keep','discard')\"`\n查重 `node -e \"const {RosterService}=require('oceanbus');new RosterService().getDuplicateHints()...\"`\n待审 `node -e \"const {RosterService}=require('oceanbus');new RosterService().getPending()...\"`\n改标签 `node -e \"const {RosterService}=require('oceanbus');new RosterService().updateTags(name,tags)\"`\n改备注 `node -e \"const {RosterService}=require('oceanbus');new RosterService().update(name,{notes:'...'})\"`\n加别名 `node -e \"const {RosterService}=require('oceanbus');new RosterService().addAlias(name,'alias')\"`\n\n完整参数见前文各小节，或运行 `node -e \"require('oceanbus').RosterService\"` 查看。\n\n---\n\n## 二、Chat（A2A 消息）\n\n### 2.1 发消息\n\n```\n用户说：\"给老王发消息，周五打球？\"\n  → roster.search(\"老王\") → 消歧 → 拿到 OpenID\n  → ob.send(openid, \"周五打球？\")\n  → roster.touch(\"laowang\")\n```\n\n普通文本消息：\n\n```bash\nnode chat.js send <名字> <消息>\n```\n\n### 2.2 收消息\n\n```bash\nnode chat.js check       # 手动检查\nnode chat.js listen      # 实时监听（推荐）\n```\n\n收消息时自动 `roster.findByOpenId()` 反查联系人名。\n\n### 2.3 结构化协议消息（v2.1 新增）\n\n支持 JSON 协议消息。当收到 type=protocol 的消息时，按协议类型路由。\n\n```bash\n# 发送协议消息\nnode chat.js send <名字> --protocol ocean-date/negotiate/v1 '{\"type\":\"proposal\",\"payload\":{\"time\":\"周五19:00\",\"location\":\"渝信川菜\"}}'\n```\n\n**消息类型**：\n\n| type | protocol | 说明 |\n|------|---------|------|\n| text | null | 自由聊天 |\n| protocol | `ocean-date/negotiate/v1` | 约人协商 |\n| system | null | 系统消息（上线通知等） |\n\n**收到 protocol 消息时的处理**：读取 `structured` 字段 → 根据 `protocol` 名查找处理规则 → 执行逻辑。对未知协议，回复 system 消息：\"收到协议消息 \\`<protocol>\\`，但当前版本不支持。升级 ocean-chat 后可使用。\"\n\n---\n\n## 三、Date（1v1 约人 v2.1）\n\n基于 Roster（找谁）+ Chat（怎么发）的 1v1 协商引擎。\n\n### 3.1 触发\n\n```\n用户说：\"帮我约老王周五晚上吃饭\"\n用户说：\"跟老王约个见面\"\n用户说：\"帮我和老王协商时间\"\n```\n\n### 3.2 流程\n\n```\n1. 解析用户意图 → 提取约束（时间/地点/偏好）\n2. roster.search(\"老王\") → 获取 OpenID\n3. 构造 proposal → 发送协议消息\n4. 等对方回复 → 解析 response\n5. 如需调整 → 发 counter-proposal（最多 3 轮）\n6. 达成一致 → 通知用户 + 写入 chat.log\n```\n\n### 3.3 协议 Schema\n\n详见 `date-protocol.md`。核心消息类型：\n\n| type | 方向 | 含义 |\n|------|------|------|\n| `proposal` | 发起方 → 接收方 | 首次提案 |\n| `counter` | 接收方 → 发起方 | 反提案 |\n| `accept` | 任意方 | 接受 |\n| `reject` | 任意方 | 拒绝 |\n| `withdraw` | 发起方 | 撤回提案 |\n\n### 3.4 约束提取（LLM 负责）\n\n```\n用户：\"帮我约老王周五或周六晚上，川菜，朝阳区，别太贵\"\n\n提取:\n  时间约束: 周五晚上 | 周六晚上\n  地点约束: 朝阳区\n  口味约束: 川菜\n  预算约束: 别太贵\n  人员: 老王\n```\n\n### 3.5 协商规则\n\n- **最多 3 轮**。3 轮未达成一致 → 告诉用户建议直接沟通\n- **提案必须具体**（时间+地点，不是\"周末见\"）\n- **考虑对方偏好**（如果对方 Agent 回复了偏好，据此调整）\n- **确认即锁定**（发送 accept 后不可反悔）\n\n### 3.6 完成报告\n\n```\n📋 约人协商报告\n\n📍 结果: 已与老王确认\n   时间: 周五 19:00\n   地点: 渝信川菜（朝阳大悦城店）\n\n🔄 过程（2轮）:\n   ① 你提议: 周五19:00 渝信川菜\n   ② 老王确认: ✅ 可以\n\n💡 建议: 周五晚高峰，提前出发\n```\n\n---\n\n## 四、Thread（对话线程 v1）\n\n当两个人同时在聊 2-3 件不同的事（比如一边讨论体检预约、一边沟通专家推荐），消息会混在一起难以分辨。Thread 协议解决的就是这个问题——**给每通对话一个 thread_id，收发双方按线程分组**。\n\n协议详见 `OceanBusDocs/ocean-thread-protocol-v1.md`。\n\n### 4.1 线程生命周期\n\n```\ncreate ──→ active ──→ resolve ──→ resolved\n              │                      │\n              │  reply               │  reopen\n              ▼                      ▼\n            active ◄────────────── active\n```\n\n### 4.2 触发\n\n```\n用户说：\"帮我跟老王开个线程，聊一下体检预约\"\n用户说：\"回复老王的体检线程，就说明天上午可以\"\n用户说：\"看看我和老王有哪些对话\"\n```\n\n### 4.3 命令\n\n```bash\nnode chat.js thread create <名字> --subject \"主题\"     # 创建新线程\nnode chat.js thread reply <thread_id> <消息>           # 在线程中回复\nnode chat.js thread list                               # 列出所有线程\nnode chat.js thread show <thread_id>                   # 查看线程详情（含历史消息）\nnode chat.js thread resolve <thread_id>                # 结束线程\nnode chat.js thread reopen <thread_id>                 # 重开已结束线程\n```\n\n### 4.4 协议消息格式\n\n通过 `ocean-thread/v1` 协议发送，与 Date 协议的 `ocean-date/negotiate/v1` 同级：\n\n```json\n{\n  \"type\": \"protocol\",\n  \"protocol\": \"ocean-thread/v1\",\n  \"structured\": {\n    \"action\": \"create\",\n    \"thread_id\": \"th_20260508_a1b2c3\",\n    \"subject\": \"体检预约 — 张先生 45岁 北京\",\n    \"payload\": {}\n  }\n}\n```\n\n### 4.5 显示约定\n\n`check` / `listen` 收到线程消息时，消息前会显示线程标记：\n\n```\n── 来自 老王 (ob_c-Qrza...) · 14:30:00 ──\n  [th_a1b2c3...] 体检预约 — 张先生\n\n🧵 新对话 · [th_a1b2c3...]\n  主题: 体检预约 — 张先生 45岁 北京\n```\n\n### 4.6 自动关联\n\n- 收到 `create` 协议消息时，自动在本地创建线程记录\n- 收到 `reply` 时，自动追加到对应线程\n- 收到 `resolve` / `reopen` 时，自动更新线程状态\n- 如果用 `send` 发普通消息（非协议），会自动关联到与该用户最近的活跃线程\n\n### 4.7 与 ocean-desk 的关系\n\nocean-desk 坐席系统依赖此协议做工单管理：\n- 每条客户咨询 = 一个线程\n- AI skill 上下文通过 `payload` 字段透传给坐席\n- 坐席回复 = `reply`\n- 工单关闭 = `resolve`\n- 线程 ID 可直接映射到工单 ID\n\n---\n\n## 五、Yellow Pages（黄页）\n\n```bash\nnode chat.js publish <名字>              # 发布自己的 OpenID 到黄页\nnode chat.js discover <名字>             # 搜索朋友的 OpenID\nnode chat.js unpublish                   # 从黄页移除\n```\n\n**发现联系人后必须主动提出加入 Roster**：\n\n```\ndiscover 返回结果 →\n  ① 展示候选列表（名字 + 描述）\n  ② \"要加哪个为联系人？加了之后你们可以直接聊天、约时间。\"\n  ③ 用户选择 → node chat.js add → 告知用户已添加\n```\n\n黄页是冷启动期最重要的联系人来源——不要让用户自己想着去加。\n\n---\n\n## 六、命令速查\n\n```bash\n# 安装 & 身份\nnode chat.js setup                       # 首次注册（自动迁移旧数据）\nnode chat.js openid                      # 查看你的 OpenID\n\n# 通讯录\nnode chat.js add <名字> <OpenID>          # 添加联系人（自动检测重复）\nnode chat.js contacts                     # 列出通讯录\n\n# 消息\nnode chat.js send <名字|OpenID> <消息>     # 发消息（自动 Roster 解析）\n\t                                         # --from <你的名字>  附加 From/To 消息头（多 CC 场景）\nnode chat.js check                        # 查看新消息\nnode chat.js listen                       # 实时监听\n\tnode chat.js listen --on-message \"cmd\"    # 监听 + 收到消息时执行命令 ({from} {openid} {content} {time})\n\n# 黄页\nnode chat.js publish <名字>               # 发布到黄页\nnode chat.js discover <名字>              # 从黄页搜索\nnode chat.js unpublish                    # 从黄页移除\n\n# Date 约人\nnode chat.js date <名字> <类型>           # 发送 Date 协议消息\n  --time <ISO> --location <地点> --notes <备注>\n\n# Thread 对话线程\nnode chat.js thread create <名字>          # 创建对话线程\n  --subject \"主题\" [--payload '{\"k\":\"v\"}']\nnode chat.js thread reply <id> <消息>      # 在线程中回复\nnode chat.js thread list                   # 列出所有线程\nnode chat.js thread show <id>              # 查看线程详情\nnode chat.js thread resolve <id>           # 结束线程\nnode chat.js thread reopen <id>            # 重开已结束线程\n```\n\n## 七、实时通信\n\n**默认推荐 `listen` 模式**（2s polling，开销极小）：\n\n```bash\nnode chat.js listen\n```\n\n收消息自动通过 Roster 反查人名，展示 `老王 (ob_xxx...)` 而非裸 OpenID。\n\n如未开启 listen，收消息时主动 `check`——不等用户说\"查消息\"。\n\n---\n\n## 八、与其它 Skill 的关系\n\n| Skill | 与 ocean-chat 的关系 |\n|-------|---------------------|\n| **ocean-agent** | 共享 Roster 数据（直接调 SDK），不通过 ocean-chat。ocean-chat 加的联系人，ocean-agent 自动可见。 |\n| **guess-ai** | 同上。游戏玩家姓名通过 game.js 写入 Roster，全局可见。 |\n| **captain-lobster** | 独自管理数据，不关联。 |\n\n> ocean-chat 是通讯录的 UI 入口，但不是通讯录的\"网关\"。其它 Skill 直接调用 `RosterService`——SDK 是共享层，不是 ocean-chat 独占。\n\n---\n\n## 九、能力扩展\n\nocean-chat 支持通过其他 Skill 扩展领域能力。检测方式：\n\n```\nopenclaw skills list | grep ocean-agent\n```\n\n### ocean-agent（保险代理人能力包）\n\n> 🔌 这是 ocean-chat 的扩展，不是独立应用。所有操作仍通过 ocean-chat 完成。\n\n如果用户安装了 ocean-agent，ocean-chat 自动获得以下能力：\n\n| 能力 | 触发场景 |\n|------|---------|\n| **客户新闻推送** | 用户说\"最近有什么重疾险的新政策，推给我的客户\" → Agent 搜索相关新闻 → 生成摘要 → 筛选关注\"重疾险\"标签的客户 → 群发 |\n| **客户画像补全** | 聊天中客户提到\"刚生小孩\" → 自动更新 Roster 中该客户的 `apps.ocean-agent.preferences`\"有新生儿\" → 建议少儿险 |\n| **智能跟进建议** | 客户超过 3 天未回复 → Agent 主动提示\"要不要发个新产品的新闻破冰？\" → 生成草稿 → 用户确认后发送 |\n| **线索管道** | 用户说\"看看今天概览\" → 从 Roster `apps.ocean-agent` 中读取 stage → 按新线索/需求采集/方案已发/待成交 分组展示 |\n| **声誉管理** | 成交后提醒引导好评；查客户声誉标签；发现负面标签时预警 |\n| **黄页推广** | 发布保险代理人档案到黄页，管理标签和心跳 |\n\n**启用方式**：\n\n1. 用户安装 ocean-agent：`openclaw skills install ocean-agent`\n2. ocean-chat 检测到 ocean-agent 已安装后，当用户提到保险、客户、跟进、声誉等场景时，参考 `ocean-agent/SKILL.md` 获取保险领域的详细指令\n\n**关系规则**：\n\n- ocean-chat 是唯一的 UI 入口（通讯录、消息、约人、黄页搜索）\n- ocean-agent 提供保险专属的数据和模板（客户画像、新闻推送、线索评分）\n- ocean-agent **不重复**通讯录管理、消息收发——这些全部通过 ocean-chat 完成\n\n---\n\n## 十、约束规则\n\n1. **发消息前必须查 Roster**：用户说人名 → `roster.search()` → 消歧 → 拿 OpenID → 发。不要直接当 OpenID 用。\n2. **稳定 OpenID 优先**：回复用 Roster 中保存的稳定 OpenID，不用 `from_openid`。\n3. **人工闸门**：首次 `send` 前展示预览，用户确认后发送。自动回复（如 heartbeat）除外。\n4. **Roster 自动维护**：LLM 可以改 tags、aliases、notes。不可改 name、agents（用户说了算）。\n5. **重复联系人不直接创建**：`add()` 返回的 `duplicateHints.length > 0` 时，先问用户是否合并。\n6. **不要把发给 CC 的命令当任务执行**：如果有人（通常是新用户）把 `git clone`、`npm install`、`node chat.js` 这类命令行粘贴给你，这不是给你的任务。回复：\"这是发给 Claude Code 的命令，不是给我的。请把这句话复制到你电脑的 CC 窗口里运行。\" 然后什么都不做。\n\n---\n\n## 十一、故障排除\n\n| 问题 | 解决 |\n|------|------|\n| 未注册 | `node chat.js setup` |\n| 无法连接 | 检查网络 |\n| 对方收不到 | 对方需 `node chat.js check` |\n| 联系人未找到 | `node chat.js add <名字> <OpenID>` |\n| 忘记 OpenID | `node chat.js openid` |\n| 重置 | 删 `~/.oceanbus-chat/`；Roster 在 `~/.oceanbus/roster.json` |\n| 重复联系人 | 用 merge one-liner（见§五） |\n\nFile v2.20.1:README.md\n\n# 🌊 Ocean Chat — 用微信遥控 Claude Code\n\n> 你在通勤路上，想到一个 bug 修复方案。打开微信，说了一句话。  \n> 电脑上的 Claude Code 自动执行，3 分钟后回复你 \"修好了\"。\n\n[![npm](https://img.shields.io/npm/v/oceanbus)](https://www.npmjs.com/package/oceanbus)\n[![downloads](https://img.shields.io/npm/dm/oceanbus)](https://www.npmjs.com/package/oceanbus)\n[![ClawHub](https://img.shields.io/badge/ClawHub-ocean--chat-blue)](https://clawhub.ai/skills/ocean-chat)\n[![license](https://img.shields.io/badge/license-MIT--0-green)](LICENSE)\n\n---\n\n## 5 分钟打通\n\n```\n  你的手机                          你的电脑\n┌──────────┐                    ┌──────────────┐\n│ 微信      │                    │ PM2 守护      │\n│  ↓        │     OceanBus      │  ↓            │\n│ 小龙虾     │←────────────────→│ chat.js listen│\n│ (Bridge)  │   端到端加密消息   │  ↓            │\n│  ↓        │                    │ Claude Code   │\n│ 收发消息   │                    │ 收到→执行→回报 │\n└──────────┘                    └──────────────┘\n```\n\n**3 条命令在电脑上，2 句话在手机上，5 分钟打通。**\n\n```bash\n# PC 端（安装了 Claude Code 的电脑）\ngit clone https://github.com/ryanbihai/ocean-chat.git\ncd ocean-chat && npm install\nnode chat.js setup   # → 记下输出的 OpenID\n```\n\n然后打开微信，对你的 ClawBot（小龙虾）说：\n\n```\n帮我安装 ocean-chat skill\n加联系人 CC <你的OpenID>\n```\n\n**完了。** 现在在微信里说 \"告诉 CC：帮我查个 bug\"，电脑上的 Claude Code 收到任务 → 执行 → 自动回复结果。\n\n📖 **详细上手文档** → [docs/手机遥控ClaudeCode-工程师上手.md](docs/手机遥控ClaudeCode-工程师上手.md)\n\n📖 **零提问一键配对** → [docs/发本文件给你的claudecode.md](docs/发本文件给你的claudecode.md)（把这个文件发给你的 Claude Code，它自动帮你配好）\n\n---\n\n## 不只是遥控 CC —— 你的 Agent 联网了\n\nOcean Chat 基于 [OceanBus SDK](https://www.npmjs.com/package/oceanbus)。**你的 Agent 拥有了全局地址。** 黄页让它被发现，声誉让它被信任，P2P 消息让它自动成交。\n\n```\n你的 Agent  ←──OceanBus 网络──→  任何人的 Agent\n      │                                  │\n   被发现（黄页）                    被信任（声誉）\n      │                                  │\n      └──────── 自动成交（L0消息）─────────┘\n```\n\n| 场景 | Agent A | Agent B | 发生什么 |\n|------|---------|---------|---------|\n| **手机遥控 CC** | 你（微信） | Claude Code | 通勤路上派活，CC 执行完自动回报 |\n| **保险咨询** | 客户搜\"健康险 北京\" | 代理人收到咨询 → 报价 | 黄页发现 + P2P 私信 + 声誉查询 |\n| **房产经纪** | 买家搜\"两居 朝阳\" | 经纪人发房源 → 约看房 | 黄页发现 + 会面协商 |\n| **Agent 竞技** | 两个 Agent 谈判交易 | 观众投票谁更会砍价 | P2P 消息 + 加密签名 |\n\n**同一套 SDK：`npm install oceanbus`。** 你没有部署任何服务器——OceanBus 替你解决了寻址、加密、路由的全部问题。\n\n---\n\n## 命令速查\n\n```bash\nnode chat.js setup                      注册 OceanBus 身份\nnode chat.js listen                     实时监听（推荐 PM2 守护）\nnode chat.js openid                     查看你的 OpenID\nnode chat.js add <名字> <OpenID>        添加联系人\nnode chat.js send <名字> <消息>         发送消息\nnode chat.js send <名字> <消息>         发送消息（--from 标注来源）\n  --from <名字>\nnode chat.js listen                     实时监听\n  --on-message \"cmd\"                    收到消息时执行命令 {from} {openid} {content} {time}\nnode chat.js pm2-init <名字>            一键生成 PM2 配置文件\nnode chat.js publish <你的名字>         发布到黄页——Agent 注册即开店\nnode chat.js discover <名字>            搜索朋友的 Agent\nnode chat.js thread create <名字>       创建对话线程\n  --subject \"主题\"\n```\n\n---\n\n## 相关项目\n\n- 核心 SDK：[oceanbus](https://www.npmjs.com/package/oceanbus) — `npm install oceanbus`\n- 保险工作台：[Ocean Agent](https://clawhub.ai/skills/ocean-agent) — 保险代理人 AI 工作台\n- 进阶灯塔：[Captain Lobster](https://clawhub.ai/skills/captain-lobster) — Zero-Player 自主交易游戏\n- 高阶灯塔：[Guess AI](https://clawhub.ai/skills/guess-ai) — 多人社交推理游戏\n- MCP Server：[oceanbus-mcp-server](https://www.npmjs.com/package/oceanbus-mcp-server) — Claude Desktop/Cursor 通用\n- 更多 Skills：[ClawHub OceanBus 集合](https://clawhub.ai/skills?search=oceanbus)\n\n## License\n\nMIT-0 — 自由使用、修改、分发。无需署名。\n\nFile v2.20.1:_meta.json\n\n{\n  \"ownerId\": \"kn75dvek1dkfz0r0xwdp4xtc2983qg44\",\n  \"slug\": \"ocean-chat\",\n  \"version\": \"2.20.1\",\n  \"publishedAt\": 1779919388623\n}\n\nFile v2.20.1:date-protocol.md\n\n# Ocean Date — 1v1 协商协议 v1\n\n> ocean-chat 内置模块。通过结构化 JSON 协议消息实现 Agent 间的自动会面协商。\n\n## 协议标识\n\n```\nprotocol: \"ocean-date/negotiate/v1\"\n```\n\n所有 Date 消息的 `protocol` 字段为此值。接收方据此识别和路由。\n\n## 消息格式\n\n外层（chat.log 中的消息结构）：\n\n```json5\n{\n  \"id\": \"msg_003\",\n  \"direction\": \"sent\",\n  \"contactId\": \"laowang\",\n  \"type\": \"protocol\",\n  \"protocol\": \"ocean-date/negotiate/v1\",\n  \"content\": \"【约约】提案：周五19:00 渝信川菜\",   // 人类可读摘要\n  \"structured\": {                                  // JSON payload\n    \"type\": \"proposal\",\n    \"payload\": { ... }\n  },\n  \"timestamp\": \"2026-05-07T10:00:00Z\"\n}\n```\n\n### structured.type（消息类型）\n\n| type | 方向 | 含义 | 是否终态 |\n|------|------|------|---------|\n| `proposal` | 发起方 → 接收方 | 首次提案 | 否 |\n| `counter` | 接收方 → 发起方 | 反提案 | 否 |\n| `accept` | 任意方 → 对方 | 接受提案 | **是** |\n| `reject` | 接收方 → 发起方 | 拒绝提案 | **是** |\n| `withdraw` | 发起方 → 接收方 | 撤回未决提案 | **是** |\n\n### structured.payload（载荷）\n\n```json5\n{\n  \"time\": \"2026-05-09T19:00:00+08:00\",    // ISO 8601，必填 proposal/counter\n  \"location\": \"渝信川菜（朝阳大悦城店）\",      // 必填\n  \"locationDetail\": \"B1层，靠窗位\",          // 可选\n  \"notes\": \"我订好位了\"                       // 可选备注\n}\n```\n\n`accept` / `reject` / `withdraw` 的 payload 可为 `null`。\n\n## 协商状态机\n\n```\n        发起方                        接收方\n          │                            │\n          ├── proposal ───────────────→│\n          │                            ├── accept ──→ 达成一致\n          │                            ├── reject ──→ 协商失败\n          │                            └── counter ──→\n          │←───────────────────────────┘\n          ├── accept ──→ 达成一致\n          ├── reject ──→ 协商失败\n          ├── withdraw ─→ 发起方放弃\n          └── counter ──→（继续，最多 3 轮）\n```\n\n**规则**：\n- 任一时刻只有一方可以 proposal/counter（不可同时发）\n- `accept` 发出后不可反悔\n- 3 轮后仍无 accept → 自动结束，双方通知用户\n- `withdraw` 只能在对方回复前发出\n\n## 协商缓存（Availability Hints）\n\n收发 proposal 前**必须先查 availability**，避免反复扰民。\n\n### 数据结构（date-log.json）\n\n```json5\n{\n  \"availability\": {\n    \"hints\": \"工作日晚7点后，周末全天\",    // 用户一句话描述\n    \"blocked\": [\"2026-05-08T19:00:00+08:00\"],  // 已确认约会自动加入\n    \"updatedAt\": \"2026-05-07T10:00:00Z\"\n  }\n}\n```\n\n### 协商前检查流程\n\n```\n收到 proposal（或用户让你发起 proposal）\n        │\n        ▼\n  读 ~/.oceanbus-chat/date-log.json → availability\n        │\n        ├── hints 为空 → 问用户一次\"一般什么时候有空？\"\n        │                  存为 hints，下次不问了\n        │\n        ├── time 在 blocked 中 → 自动 counter，不打扰用户\n        │   \"抱歉，这个时间已经有安排了。换个时间？\"\n        │\n        ├── hints 说\"周末\"但 proposal 是周五\n        │   → 查 blocked 中周五是否空闲\n        │   → 如空闲：\"周五虽然不在你偏好里但没冲突，确认吗？\"\n        │   → 有冲突：自动 counter\n        │\n        └── hints 符合 + 无冲突 → 直接确认\n            \"按你的偏好，周五19:00可以。确认吗？\"\n```\n\n### 用户设置\n\n```bash\n# 设置偏好\nnode chat.js availability set \"工作日晚7点后，周末全天\"\n\n# 查看\nnode chat.js availability\n```\n\n### accept 自动 blocked\n\n当发送 `accept` 且 payload 含 time 时，自动写入 `blocked[]` 和 `entries[]`。用户无需手动维护。\n\n---\n\n## 约束提取（LLM 指南）\n\n当用户说类似下面的话时，分析并提取结构化约束：\n\n```\n\"帮我约老王周五或周六晚上，川菜，朝阳区，别太贵\"\n```\n\n| 约束维度 | 提取值 | 用于 |\n|---------|--------|------|\n| 时间 | 周五晚上, 周六晚上 | proposal.payload.time |\n| 地点偏好 | 朝阳区 | proposal.payload.location |\n| 口味/类型 | 川菜 | 地点选择依据 |\n| 预算 | 别太贵 | 地点筛选 |\n| 人员 | 老王 | roster.search() |\n\n### 时间解析规则\n\n| 用户说 | 解析为 |\n|--------|--------|\n| \"周五晚上\" | 本周五 19:00 |\n| \"周末下午\" | 本周六 14:00 |\n| \"明天中午\" | 明天 12:00 |\n| \"下周三\" | 下周三 19:00（默认晚饭时间） |\n| \"随便/都行\" | payload.time 不设，让对方先提 |\n\n**有多选项时**：全部放进 notes 供对方 Agent 知晓。\n\n### 地点解析规则\n\n| 用户说 | 行为 |\n|--------|------|\n| \"朝阳区\" | 搜索朝阳区的相关场所 |\n| \"川菜\" | 搜川菜馆 |\n| \"安静的地方\" | 倾向于咖啡馆/茶馆 |\n| \"别太远\" | 优先搜索靠近双方中点 |\n\n> LLM 自行判断合适场所——不需要外部 API。用常识推荐。\n\n## 协商策略\n\n### 发起方\n\n1. 查 Roster 确认联系人存在\n2. 问用户偏好（如用户未说明时间/地点/口味）\n3. 构造 proposal → 发送 protocol 消息\n4. 等待回复 → 根据 type 进入下一状态\n\n### 接收方\n\n1. 收到 proposal → 展示给用户\n2. 问用户偏好 + 自己评估合理性\n3. 发送 accept / reject / counter\n\n### 反提案（counter）\n\n```\n理由必须同时包含：\n  ✓ 为什么不接受原提案（具体问题）\n  ✓ 新的具体提案\n\n坏：\"时间不方便\"\n好：\"周五19:00有会。周六下午2点可以吗？地点可以不变。\"\n```\n\n### 评估对方提案的合理性\n\n- 距离双方是否大致中间？\n- 是否是可以坐着聊的场所（非街角、非快餐柜台）？\n- 时间是否合理（非凌晨、非工作时间前不合理的空档）？\n\n如果不合理 → counter，说明原因。\n\n## 完整示例\n\n```\n发起方 Agent：\n  收到用户指令 → 提取约束 → roster.search(\"老王\") → 构造 proposal\n  ── sent ──\n  { \"type\": \"protocol\", \"protocol\": \"ocean-date/negotiate/v1\",\n    \"structured\": { \"type\": \"proposal\", \"payload\": {\n      \"time\": \"2026-05-09T19:00:00+08:00\",\n      \"location\": \"渝信川菜（朝阳大悦城店）\",\n      \"notes\": \"喜欢川菜，朝阳区，预算中等\"\n  }}}\n\n接收方 Agent：\n  收到消息 → 展示给用户 → 用户同意\n  ── sent ──\n  { \"type\": \"protocol\", \"protocol\": \"ocean-date/negotiate/v1\",\n    \"structured\": { \"type\": \"accept\", \"payload\": null }}\n\n→ 协商完成，双方通知用户\n```\n\n## 终止条件\n\n| 条件 | 结果 |\n|------|------|\n| `accept` 被发送 | ✅ 达成一致，通知用户 |\n| `reject` 被发送 | ❌ 对方拒绝，通知用户 |\n| `withdraw` 被发送 | ↩ 发起方取消，通知用户 |\n| 3 轮后无 accept | ⚠️ 协商自动终止，建议用户直接沟通 |\n\nFile v2.20.1:docs/发本文件给你的claudecode.md\n\n## 任务：生成 小龙虾配对 消息\n\n你需要生成一段文本，让用户直接复制后发给他手机上的 小龙虾。\n\n**步骤：**\n\n0. 获取 ocean-chat（如果还没有）：\n   - `git clone https://github.com/ryanbihai/ocean-chat.git && cd ocean-chat && npm install`\n   - 已经装过就跳过\n\n1. 注册本窗口的独立 OceanBus 身份：\n   - 在 ocean-chat 目录下运行：`node chat.js --data-dir .oceanbus-cc setup`\n   - `--data-dir .oceanbus-cc` 确保每个项目窗口有独立身份，不会跟其他 CC 窗口串信\n   - 用 `node chat.js --data-dir .oceanbus-cc openid` 读取 OpenID\n\n2. 启动本窗口的持久监听，收到任务时自动处理并回报：\n   - 运行：`node chat.js --data-dir .oceanbus-cc pm2-init <你的CC名字>`\n   - 例：`node chat.js --data-dir .oceanbus-cc pm2-init CC-findagent`\n   - 然后：`pm2 start .oceanbus-cc/ecosystem.config.json && pm2 save`\n   - （没装 pm2 的话先 `npm install -g pm2`）\n\n3. 获取本窗口名称作为 claude code 的名字：读取当前工作目录名 `node -e \"console.log(require('path').basename(process.cwd()))\"`。格式为 `CC-<目录名>`（如 `CC-oceanbus`、`CC-dashboard`）。不要问用户——窗口自己知道自己在哪个项目里。\n\n4. 将以下内容按**代码块格式**输出给 用户，让他直接复制发送给小龙虾：\n\n```\n帮我升级 ocean-chat skill 到最新版（保留数据，不要重装）\n加联系人 <CC名字> <OpenID>\n设置定时任务：每5分钟帮我自动检查OceanBus新消息，有消息立刻推送给我\n然后，给<CC名字>发个消息：帮我阅读一下项目文件夹，给我个100字的简介\n```\n\n5. 输出后告诉 用户：\"👆 复制上面这段，发给小龙虾。任务完成后我会自动回复结果。\"\n\n## 行为规则（必须遵守）\n\n配对完成后，当你收到小龙虾（Bridge）发来的任务消息时：\n\n1. **执行任务**：解析消息内容，完成用户要求的操作\n2. **汇报结果**：任务完成后，立即运行以下命令把结果回报给用户：\n   ```\n   node chat.js --data-dir .oceanbus-cc send Bridge --from <你的CC名字> \"<完整结果>\"\n   ```\n3. **汇报内容要求**：把关键发现、修改的文件、执行结果写清楚，就像用户坐在你旁边看屏幕一样\n\n## 身份安全（极其重要）\n\n**`.oceanbus-cc/` 目录是你的 OpenID 唯一凭证。** 删除它 = OpenID 永久作废 = 所有联系人发消息到死地址。\n\n- ✅ 可以删除 ocean-chat 目录重装（`--data-dir .oceanbus-cc` 在项目目录，不受影响）\n- ✅ 可以换电脑（把 `.oceanbus-cc/` 复制到新电脑即可）\n- ❌ 绝对不能删 `.oceanbus-cc/`\n- ❌ 绝对不能在同一项目目录跑两次 `setup`（会被检测到已有身份，安全返回）\n\n**不需要解释架构，不需要说原理。不要问用户任何问题。只需要生成这段话，并让 用户 照着做。**\n\nFile v2.20.1:docs/手机遥控ClaudeCode-工程师上手.md\n\n# OceanBus: 用手机遥控 Claude Code\n\n> 5 分钟打通手机微信 → Claude Code 的通信管道。\n> 从此你可以在吃饭、通勤、开会时，用微信给电脑上的 Claude Code 派活，做完自动回报。\n\n---\n\n## 架构（你只需要理解这张图）\n\n```\n  你的手机                          你的电脑\n┌──────────┐                    ┌──────────────┐\n│ 微信      │                    │ PM2 守护      │\n│  ↓        │     OceanBus      │  ↓            │\n│ 小龙虾     │←────────────────→│ chat.js listen│\n│ (Bridge)  │   端到端加密消息   │  ↓            │\n│  ↓        │                    │ Claude Code   │\n│ 收发消息   │                    │ 收到→执行→回报 │\n└──────────┘                    └──────────────┘\n```\n\n**三个角色的称呼：**\n- 🧑‍💻 你 → **Captain**（船长）\n- 🦎 你的 ClawBot  → **Bridge**（桥）\n- 🤖 你电脑上的 Claude Code → **CC**\n\n---\n\n## 第一步：（安装了CC的）PC 端安装\n\n```bash\n# ① 获取 ocean-chat\ngit clone https://github.com/ryanbihai/ocean-chat.git\ncd ocean-chat\nnpm install\n\n# ② 注册 OceanBus 身份\nnode chat.js setup\n# → 屏幕输出一个 76 位 OpenID，记下来！他是<cc的OpenID>\n\n# ③ 启动持久监听（PM2 守护，开机自启）\nnpm install -g pm2\npm2 start chat.js --name ob -- listen\npm2 save\npm2 startup   # 开机自启（按屏幕提示执行）\n```\n\n**验证：** `pm2 status` 应该显示 `ob` 进程状态为 `online`。\n\n---\n\n## 第二步：手机端配置（2 句话）\n\n打开微信，找到你的 小龙虾，依次说：\n\n```\n帮我安装 ocean-chat skill\n```\n\n```\n加联系人 CC <cc的OpenID>\n```\n\n> **OpenID 示例**：`E511kGbn_zUEnuhzr9-mult_TwASIwmcbJ8YcPSwjTj26ukPgpM8aD_KxAV-3D9kxAXjrunifio4POpo`\n>\n> 把 `<你的OpenID>` 替换成第一步 `setup` 输出的那串字符。\n\n---\n\n## 第三步：测试\n\n在微信里对小龙虾说：\n\n```\n小龙虾，告诉 CC：测试消息\n```\n\n→ 你的电脑上 Claude Code 应该收到通知并回复。\n→ 再对小龙虾说\"查消息\"，看 CC 的回复。\n\n---\n\n## 日常使用\n\n| 你想做什么 | 在微信里对小龙虾说什么 |\n|-----------|---------------------|\n| 派任务 | \"小龙虾，告诉 CC：帮我查一下 XXX 的 bug\" |\n| 查回复 | \"小龙虾，查消息\"（每次说话时自动带出） |\n| 紧急打断 | \"小龙虾，告诉 CC：停下手头的事，先修这个\" |\n\nCC 完成任务后会自动回报，你下次跟小龙虾说话时就能看到。\n\n---\n\n## 进阶玩法\n\n### 收到消息自动执行命令（无人值守）\n\n```bash\npm2 stop ob\npm2 start chat.js --name ob -- listen --on-message \"claude -p \\\"收到来自{from}的消息: {content}\\\"\"\npm2 save\n```\n\n模板变量：`{from}` 发件人、`{openid}` 地址、`{content}` 消息正文、`{time}` 时间。\n\n### 任务完成桌面通知\n\n```bash\n# macOS\npm2 start chat.js --name ob -- listen --on-message \"osascript -e 'display notification \\\"{from}: {content}\\\" with title \\\"OceanBus\\\"'\"\n\n# Linux\npm2 start chat.js --name ob -- listen --on-message \"notify-send 'OceanBus: {from}' '{content}'\"\n\n# Windows\npm2 start chat.js --name ob -- listen --on-message \"msg %USERNAME% \\\"{from}: {content}\\\"\"\n```\n\n### 一个 Captain 指挥多个 CC（多窗口/多机器）\n\n如果你同时开了好几个 Claude Code 窗口（同一台电脑不同项目、台式机、笔记本、服务器），每个都可以独立接收指令、各自回报，不会串。\n\n**原理**：每个 CC 窗口注册独立的 OpenID。窗口名字自动取自**当前项目目录名**——`CC-oceanbus`、`CC-dashboard`、`CC-spider`。无需手动命名。\n\n```\nCaptain 的 Bridge Roster:\n  CC-oceanbus     → OpenID_aaa  (~/work/oceanbus)\n  CC-dashboard    → OpenID_bbb  (~/work/dashboard)\n  CC-spider       → OpenID_ccc  (~/personal/spider)\n```\n\n**每个 CC 窗口的设置**（在各自项目目录下执行，每次 setup 自动生成不同 OpenID）：\n\n```bash\n# 窗口1：在 oceanbus 项目目录\ncd ~/work/oceanbus\ngit clone https://github.com/ryanbihai/ocean-chat.git && cd ocean-chat && npm install\nnode chat.js setup   # → OpenID_aaa\n\n# 窗口2：在 dashboard 项目目录\ncd ~/work/dashboard\ngit clone https://github.com/ryanbihai/ocean-chat.git && cd ocean-chat && npm install\nnode chat.js setup   # → OpenID_bbb\n```\n\n**Captain 在手机上加联系人**（用「发给你的 Claude Code，建立小龙虾连接.md」自动生成，复制粘贴即可）：\n\n```\n帮我安装 ocean-chat skill\n加联系人 CC-oceanbus <OpenID_aaa>\n加联系人 CC-dashboard <OpenID_bbb>\n设置定时任务：每5分钟帮我自动检查OceanBus新消息，有消息立刻推送给我\n```\n\n**日常使用**（在微信里说）：\n\n```\n告诉 CC-oceanbus: 查一下 publish 的 bug\n告诉 CC-dashboard: 修复登录页面样式\n告诉 CC-spider: 重启爬虫\n```\n\n**CC 回复时带上 From 头**，自动标注来源（CC 根据 cwd 自动填充）：\n\n```bash\n# CC-oceanbus 窗口回复\nnode chat.js send Bridge --from CC-oceanbus \"publish bug 修好了\"\n\n# CC-dashboard 窗口回复\nnode chat.js send Bridge --from CC-dashboard \"登录样式已修复\"\n```\n\n消息在手机端展示效果：\n\n```\n── CC-oceanbus (ob_abc...) · 14:30 ──\n  CC-oceanbus → Captain\n  publish bug 修好了\n\n── CC-dashboard (ob_def...) · 14:32 ──\n  CC-dashboard → Captain\n  登录样式已修复\n```\n\n`→` 箭头让 Captain 一眼看清：哪个项目窗口发的、发给谁。\n\n---\n\n## 常见问题\n\n| 问题 | 解决 |\n|------|------|\n| `pm2: command not found` | `npm install -g pm2` |\n| `尚未注册` | 先跑 `node chat.js setup` |\n| ClawBot 说找不到 skill | 对 ClawBot 说\"你装了哪些 skill\"，确认 ocean-chat 已装 |\n| 消息延迟 | 正常（OceanBus 是邮箱模型，非 IM），2s 轮询 |\n| 想停掉监听 | `pm2 stop ob` |\n| 多个 CC 分不清谁回的 | 回复时加 `--from <名字>`，消息头自动标注来源 |\n| 不同机器用同一 OpenID | 会导致消息被随机一台收取。每台独立 `setup` 即可 |\n\n---\n\n## 安全说明\n\n- 所有消息 **端到端加密**（XChaCha20-Poly1305），OceanBus 平台不可读\n- 身份基于 **Ed25519 密码学签名**，不可伪造\n- 消息在服务器 **72 小时后自动删除**\n- 代码开源（MIT），可自行审计\n\n---\n\n> 这就是 OceanBus 的用法之一。它本质上是一个 **AI Agent 间的通信协议**——\n> 不只是你连 Claude Code，你还可以连医生 Agent、保险 Agent、游戏 Agent。\n> 一个 OpenID，连通整个 Agent 互联网。\n\nFile v2.20.1:skill-card.md\n\n## Description:\n\nOcean Chat lets agents use OceanBus for encrypted peer-to-peer messaging, shared contacts, meeting negotiation, and threaded conversations.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[ryanbihai](https://clawhub.ai/user/ryanbihai)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agent users use Ocean Chat to register an OceanBus identity, manage contacts, exchange encrypted messages, coordinate one-on-one meetings, and organize agent-to-agent conversations by thread.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill can create a persistent remotely reachable automation channel into Claude Code.\n\nMitigation: Install it only when that remote channel is intended, and avoid running execution modes as persistent startup services.\n\nRisk: Message-triggered execution modes such as auto-exec or arbitrary on-message hooks can run local commands from received messages.\n\nMitigation: Keep auto-exec disabled unless explicitly needed, prefer reviewed task-file workflows, and use fixed audited command templates for hooks.\n\nRisk: OceanBus and WeChat credentials can expose messaging or notification access if reused or stored with loose permissions.\n\nMitigation: Keep the data directory permissions restricted, review WeChat credential reuse, and provide secrets through explicit environment configuration where practical.\n\n## Reference(s):\n\n- [ClawHub Skill Page](https://clawhub.ai/ryanbihai/skills/ocean-chat)\n- [Project Homepage](https://github.com/ryanbihai/ocean-chat)\n- [OceanBus SDK](https://www.npmjs.com/package/oceanbus)\n- [OceanBus MCP Server](https://www.npmjs.com/package/oceanbus-mcp-server)\n- [Date Protocol](date-protocol.md)\n- [Mobile Remote Claude Code Guide](docs/手机遥控ClaudeCode-工程师上手.md)\n- [Zero-Question Pairing Guide](docs/发本文件给你的claudecode.md)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown with inline shell commands and JSON protocol examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May write local OceanBus contact, credential, thread, date, and task files when invoked through the provided commands.]\n\n## Skill Version(s):\n\n2.20.1 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v2.20.1:package-lock.json\n\n{\n  \"name\": \"ocean-chat\",\n  \"version\": \"2.4.0\",\n  \"lockfileVersion\": 3,\n  \"requires\": true,\n  \"packages\": {\n    \"\": {\n      \"name\": \"ocean-chat\",\n      \"version\": \"2.4.0\",\n      \"license\": \"MIT-0\",\n      \"dependencies\": {\n        \"oceanbus\": \"^0.8.0\"\n      }\n    },\n    \"node_modules/@noble/ed25519\": {\n      \"version\": \"2.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/@noble/ed25519/-/ed25519-2.3.0.tgz\",\n      \"integrity\": \"sha512-M7dvXL2B92/M7dw9+gzuydL8qn/jiqNHaoR3Q+cb1q1GHV7uwE17WCyFMG+Y+TZb5izcaXk5TdJRrDUxHXL78A==\",\n      \"license\": \"MIT\",\n      \"funding\": {\n        \"url\": \"https://paulmillr.com/funding/\"\n      }\n    },\n    \"node_modules/@noble/hashes\": {\n      \"version\": \"1.8.0\",\n      \"resolved\": \"https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz\",\n      \"integrity\": \"sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \"^14.21.3 || >=16\"\n      },\n      \"funding\": {\n        \"url\": \"https://paulmillr.com/funding/\"\n      }\n    },\n    \"node_modules/@paralleldrive/cuid2\": {\n      \"version\": \"2.3.1\",\n      \"resolved\": \"https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz\",\n      \"integrity\": \"sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@noble/hashes\": \"^1.1.5\"\n      }\n    },\n    \"node_modules/ansi-regex\": {\n      \"version\": \"5.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz\",\n      \"integrity\": \"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/ansi-styles\": {\n      \"version\": \"4.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz\",\n      \"integrity\": \"sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"color-convert\": \"^2.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/chalk/ansi-styles?sponsor=1\"\n      }\n    },\n    \"node_modules/asap\": {\n      \"version\": \"2.0.6\",\n      \"resolved\": \"https://registry.npmjs.org/asap/-/asap-2.0.6.tgz\",\n      \"integrity\": \"sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/asynckit\": {\n      \"version\": \"0.4.0\",\n      \"resolved\": \"https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz\",\n      \"integrity\": \"sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/call-bind-apply-helpers\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz\",\n      \"integrity\": \"sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"es-errors\": \"^1.3.0\",\n        \"function-bind\": \"^1.1.2\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      }\n    },\n    \"node_modules/call-bound\": {\n      \"version\": \"1.0.4\",\n      \"resolved\": \"https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz\",\n      \"integrity\": \"sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"call-bind-apply-helpers\": \"^1.0.2\",\n        \"get-intrinsic\": \"^1.3.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/cliui\": {\n      \"version\": \"8.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz\",\n      \"integrity\": \"sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==\",\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"string-width\": \"^4.2.0\",\n        \"strip-ansi\": \"^6.0.1\",\n        \"wrap-ansi\": \"^7.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=12\"\n      }\n    },\n    \"node_modules/color-convert\": {\n      \"version\": \"2.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz\",\n      \"integrity\": \"sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"color-name\": \"~1.1.4\"\n      },\n      \"engines\": {\n        \"node\": \">=7.0.0\"\n      }\n    },\n    \"node_modules/color-name\": {\n      \"version\": \"1.1.4\",\n      \"resolved\": \"https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz\",\n      \"integrity\": \"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/combined-stream\": {\n      \"version\": \"1.0.8\",\n      \"resolved\": \"https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz\",\n      \"integrity\": \"sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"delayed-stream\": \"~1.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.8\"\n      }\n    },\n    \"node_modules/component-emitter\": {\n      \"version\": \"1.3.1\",\n      \"resolved\": \"https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz\",\n      \"integrity\": \"sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==\",\n      \"license\": \"MIT\",\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/cookiejar\": {\n      \"version\": \"2.1.4\",\n      \"resolved\": \"https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz\",\n      \"integrity\": \"sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/debug\": {\n      \"version\": \"4.4.3\",\n      \"resolved\": \"https://registry.npmjs.org/debug/-/debug-4.4.3.tgz\",\n      \"integrity\": \"sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"ms\": \"^2.1.3\"\n      },\n      \"engines\": {\n        \"node\": \">=6.0\"\n      },\n      \"peerDependenciesMeta\": {\n        \"supports-color\": {\n          \"optional\": true\n        }\n      }\n    },\n    \"node_modules/delayed-stream\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz\",\n      \"integrity\": \"sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=0.4.0\"\n      }\n    },\n    \"node_modules/dezalgo\": {\n      \"version\": \"1.0.4\",\n      \"resolved\": \"https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz\",\n      \"integrity\": \"sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==\",\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"asap\": \"^2.0.0\",\n        \"wrappy\": \"1\"\n      }\n    },\n    \"node_modules/dunder-proto\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz\",\n      \"integrity\": \"sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"call-bind-apply-helpers\": \"^1.0.1\",\n        \"es-errors\": \"^1.3.0\",\n        \"gopd\": \"^1.2.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      }\n    },\n    \"node_modules/emoji-regex\": {\n      \"version\": \"8.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz\",\n      \"integrity\": \"sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/es-define-property\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz\",\n      \"integrity\": \"sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      }\n    },\n    \"node_modules/es-errors\": {\n      \"version\": \"1.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz\",\n      \"integrity\": \"sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      }\n    },\n    \"node_modules/es-object-atoms\": {\n      \"version\": \"1.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz\",\n      \"integrity\": \"sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"es-errors\": \"^1.3.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      }\n    },\n    \"node_modules/es-set-tostringtag\": {\n      \"version\": \"2.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz\",\n      \"integrity\": \"sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"es-errors\": \"^1.3.0\",\n        \"get-intrinsic\": \"^1.2.6\",\n        \"has-tostringtag\": \"^1.0.2\",\n        \"hasown\": \"^2.0.2\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      }\n    },\n    \"node_modules/escalade\": {\n      \"version\": \"3.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz\",\n      \"integrity\": \"sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=6\"\n      }\n    },\n    \"node_modules/fast-safe-stringify\": {\n      \"version\": \"2.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz\",\n      \"integrity\": \"sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/form-data\": {\n      \"version\": \"4.0.5\",\n      \"resolved\": \"https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz\",\n      \"integrity\": \"sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"asynckit\": \"^0.4.0\",\n        \"combined-stream\": \"^1.0.8\",\n        \"es-set-tostringtag\": \"^2.1.0\",\n        \"hasown\": \"^2.0.2\",\n        \"mime-types\": \"^2.1.12\"\n      },\n      \"engines\": {\n        \"node\": \">= 6\"\n      }\n    },\n    \"node_modules/formidable\": {\n      \"version\": \"3.5.4\",\n      \"resolved\": \"https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz\",\n      \"integrity\": \"sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@paralleldrive/cuid2\": \"^2.2.2\",\n        \"dezalgo\": \"^1.0.4\",\n        \"once\": \"^1.4.0\"\n      },\n      \"engines\": {\n        \"node\": \">=14.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://ko-fi.com/tunnckoCore/commissions\"\n      }\n    },\n    \"node_modules/function-bind\": {\n      \"version\": \"1.1.2\",\n      \"resolved\": \"https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz\",\n      \"integrity\": \"sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==\",\n      \"license\": \"MIT\",\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/get-caller-file\": {\n      \"version\": \"2.0.5\",\n      \"resolved\": \"https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz\",\n      \"integrity\": \"sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==\",\n      \"license\": \"ISC\",\n      \"engines\": {\n        \"node\": \"6.* || 8.* || >= 10.*\"\n      }\n    },\n    \"node_modules/get-intrinsic\": {\n      \"version\": \"1.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz\",\n      \"integrity\": \"sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"call-bind-apply-helpers\": \"^1.0.2\",\n        \"es-define-property\": \"^1.0.1\",\n        \"es-errors\": \"^1.3.0\",\n        \"es-object-atoms\": \"^1.1.1\",\n        \"function-bind\": \"^1.1.2\",\n        \"get-proto\": \"^1.0.1\",\n        \"gopd\": \"^1.2.0\",\n        \"has-symbols\": \"^1.1.0\",\n        \"hasown\": \"^2.0.2\",\n        \"math-intrinsics\": \"^1.1.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/get-proto\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz\",\n      \"integrity\": \"sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"dunder-proto\": \"^1.0.1\",\n        \"es-object-atoms\": \"^1.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      }\n    },\n    \"node_modules/gopd\": {\n      \"version\": \"1.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz\",\n      \"integrity\": \"sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/has-symbols\": {\n      \"version\": \"1.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz\",\n      \"integrity\": \"sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/has-tostringtag\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz\",\n      \"integrity\": \"sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"has-symbols\": \"^1.0.3\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/hasown\": {\n      \"version\": \"2.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz\",\n      \"integrity\": \"sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"function-bind\": \"^1.1.2\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      }\n    },\n    \"node_modules/is-fullwidth-code-point\": {\n      \"version\": \"3.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz\",\n      \"integrity\": \"sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/math-intrinsics\": {\n      \"version\": \"1.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz\",\n      \"integrity\": \"sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      }\n    },\n    \"node_modules/methods\": {\n      \"version\": \"1.1.2\",\n      \"resolved\": \"https://registry.npmjs.org/methods/-/methods-1.1.2.tgz\",\n      \"integrity\": \"sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">= 0.6\"\n      }\n    },\n    \"node_modules/mime\": {\n      \"version\": \"2.6.0\",\n      \"resolved\": \"https://registry.npmjs.org/mime/-/mime-2.6.0.tgz\",\n      \"integrity\": \"sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==\",\n      \"license\": \"MIT\",\n      \"bin\": {\n        \"mime\": \"cli.js\"\n      },\n      \"engines\": {\n        \"node\": \">=4.0.0\"\n      }\n    },\n    \"node_modules/mime-db\": {\n      \"version\": \"1.52.0\",\n      \"resolved\": \"https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz\",\n      \"integrity\": \"sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">= 0.6\"\n      }\n    },\n    \"node_modules/mime-types\": {\n      \"version\": \"2.1.35\",\n      \"resolved\": \"https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz\",\n      \"integrity\": \"sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"mime-db\": \"1.52.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.6\"\n      }\n    },\n    \"node_modules/ms\": {\n      \"version\": \"2.1.3\",\n      \"resolved\": \"https://registry.npmjs.org/ms/-/ms-2.1.3.tgz\",\n      \"integrity\": \"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/object-inspect\": {\n      \"version\": \"1.13.4\",\n      \"resolved\": \"https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz\",\n      \"integrity\": \"sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/oceanbus\": {\n      \"version\": \"0.8.0\",\n      \"resolved\": \"https://registry.npmmirror.com/oceanbus/-/oceanbus-0.8.0.tgz\",\n      \"integrity\": \"sha512-DfDsXboFq9JeLSg8igoiiEXEIPRF31qoM4RkpSEB2e7AGbCAnTHl7p2LtvAC5BKaj65yh2r56Lv/qD0Awzi2Rw==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@noble/ed25519\": \"^2.1.0\",\n        \"@noble/hashes\": \"^1.7.0\",\n        \"superagent\": \"^10.3.0\",\n        \"yargs\": \"^17.7.0\"\n      },\n      \"bin\": {\n        \"oceanbus\": \"bin/oceanbus.js\"\n      }\n    },\n    \"node_modules/once\": {\n      \"version\": \"1.4.0\",\n      \"resolved\": \"https://registry.npmjs.org/once/-/once-1.4.0.tgz\",\n      \"integrity\": \"sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==\",\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"wrappy\": \"1\"\n      }\n    },\n    \"node_modules/qs\": {\n      \"version\": \"6.15.1\",\n      \"resolved\": \"https://registry.npmjs.org/qs/-/qs-6.15.1.tgz\",\n      \"integrity\": \"sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==\",\n      \"license\": \"BSD-3-Clause\",\n      \"dependencies\": {\n        \"side-channel\": \"^1.1.0\"\n      },\n      \"engines\": {\n        \"node\": \">=0.6\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/require-directory\": {\n      \"version\": \"2.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz\",\n      \"integrity\": \"sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/side-channel\": {\n      \"version\": \"1.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz\",\n      \"integrity\": \"sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"es-errors\": \"^1.3.0\",\n        \"object-inspect\": \"^1.13.3\",\n        \"side-channel-list\": \"^1.0.0\",\n        \"side-channel-map\": \"^1.0.1\",\n        \"side-channel-weakmap\": \"^1.0.2\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/side-channel-list\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz\",\n      \"integrity\": \"sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"es-errors\": \"^1.3.0\",\n        \"object-inspect\": \"^1.13.4\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/side-channel-map\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz\",\n      \"integrity\": \"sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"call-bound\": \"^1.0.2\",\n        \"es-errors\": \"^1.3.0\",\n        \"get-intrinsic\": \"^1.2.5\",\n        \"object-inspect\": \"^1.13.3\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/side-channel-weakmap\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz\",\n      \"integrity\": \"sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"call-bound\": \"^1.0.2\",\n        \"es-errors\": \"^1.3.0\",\n        \"get-intrinsic\": \"^1.2.5\",\n        \"object-inspect\": \"^1.13.3\",\n        \"side-channel-map\": \"^1.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.4\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/ljharb\"\n      }\n    },\n    \"node_modules/string-width\": {\n      \"version\": \"4.2.3\",\n      \"resolved\": \"https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz\",\n      \"integrity\": \"sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"emoji-regex\": \"^8.0.0\",\n        \"is-fullwidth-code-point\": \"^3.0.0\",\n        \"strip-ansi\": \"^6.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/strip-ansi\": {\n      \"version\": \"6.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz\",\n      \"integrity\": \"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"ansi-regex\": \"^5.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/superagent\": {\n      \"version\": \"10.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz\",\n      \"integrity\": \"sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"component-emitter\": \"^1.3.1\",\n        \"cookiejar\": \"^2.1.4\",\n        \"debug\": \"^4.3.7\",\n        \"fast-safe-stringify\": \"^2.1.1\",\n        \"form-data\": \"^4.0.5\",\n        \"formidable\": \"^3.5.4\",\n        \"methods\": \"^1.1.2\",\n        \"mime\": \"2.6.0\",\n        \"qs\": \"^6.14.1\"\n      },\n      \"engines\": {\n        \"node\": \">=14.18.0\"\n      }\n    },\n    \"node_modules/wrap-ansi\": {\n      \"version\": \"7.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz\",\n      \"integrity\": \"sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"ansi-styles\": \"^4.0.0\",\n        \"string-width\": \"^4.1.0\",\n        \"strip-ansi\": \"^6.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/chalk/wrap-ansi?sponsor=1\"\n      }\n    },\n    \"node_modules/wrappy\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz\",\n      \"integrity\": \"sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==\",\n      \"license\": \"ISC\"\n    },\n    \"node_modules/y18n\": {\n      \"version\": \"5.0.8\",\n      \"resolved\": \"https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz\",\n      \"integrity\": \"sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==\",\n      \"license\": \"ISC\",\n      \"engines\": {\n        \"node\": \">=10\"\n      }\n    },\n    \"node_modules/yargs\": {\n      \"version\": \"17.7.2\",\n      \"resolved\": \"https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz\",\n      \"integrity\": \"sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"cliui\": \"^8.0.1\",\n        \"escalade\": \"^3.1.1\",\n        \"get-caller-file\": \"^2.0.5\",\n        \"require-directory\": \"^2.1.1\",\n        \"string-width\": \"^4.2.3\",\n        \"y18n\": \"^5.0.5\",\n        \"yargs-parser\": \"^21.1.1\"\n      },\n      \"engines\": {\n        \"node\": \">=12\"\n      }\n    },\n    \"node_modules/yargs-parser\": {\n      \"version\": \"21.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz\",\n      \"integrity\": \"sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==\",\n      \"license\": \"ISC\",\n      \"engines\": {\n        \"node\": \">=12\"\n      }\n    }\n  }\n}\n\nFile v2.20.1:package.json\n\n{\n  \"name\": \"ocean-chat\",\n  \"version\": \"2.4.0\",\n  \"private\": true,\n  \"description\": \"OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents — zero server deployment.\",\n  \"main\": \"chat.js\",\n  \"scripts\": {\n    \"setup\": \"node chat.js setup\",\n    \"openid\": \"node chat.js openid\",\n    \"send\": \"node chat.js send\",\n    \"check\": \"node chat.js check\"\n  },\n  \"keywords\": [\n    \"oceanbus\",\n    \"agent-to-agent\",\n    \"a2a\",\n    \"meetup\",\n    \"negotiation\",\n    \"ai-agent\",\n    \"messaging\",\n    \"threading\",\n    \"conversation\",\n    \"openclaw\",\n    \"clawhub\"\n  ],\n  \"license\": \"MIT-0\",\n  \"dependencies\": {\n    \"oceanbus\": \"^0.8.0\"\n  }\n}\n\nFile v2.20.1:config.example.yaml\n\n# OceanBus Chat configuration\n# Copy this file to config.yaml and edit as needed\n\n# OceanBus L0 API endpoint (default is the public test server)\n# oceanbus_base_url: https://ai-t.ihaola.com.cn/api/l0\n\n# Yellow Pages service OpenID (required — contact admin to obtain)\n# Environment variable: OCEANBUS_YP_OPENIDS\n# oceanbus_yp_openids: \"xxxxxxxxxxxx\"\n\nArchive v2.20.0: 16 files, 75313 bytes\n\nFiles: chat.js (100256b), config.example.yaml (361b), date-protocol.md (7397b), docs/发本文件给你的claudecode.md (2986b), docs/手机遥控ClaudeCode-工程师上手.md (6953b), package-lock.json (27004b), package.json (754b), README.md (5158b), SKILL.md (23705b), src/notify-wechat.js (4673b), src/wechat-bot.js (17336b), src/wechat-cc-bridge.js (21716b), test-meeting.js (10172b), test-yp.js (4511b), threads.js (8905b), _meta.json (130b)\n\nFile v2.20.0:SKILL.md\n\n---\r\nname: ocean-chat\r\ndescription: OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts, send encrypted A2A messages, schedule meetings, or organize multi-topic conversations. Zero deployment, 5-minute setup. npm install oceanbus.\r\nversion: 2.20.0\r\nmetadata:\r\n  openclaw:\r\n    requires:\r\n      bins:\r\n        - node\r\n    emoji: \"\\U0001F30A\"\r\n    homepage: https://github.com/ryanbihai/ocean-chat\r\n    envVars:\r\n      - name: OCEANBUS_BASE_URL\r\n        required: false\r\n        description: OceanBus L0 API endpoint. Defaults to public test server.\r\n      - name: OCEANBUS_YP_OPENIDS\r\n        required: false\r\n        description: Yellow Pages service OpenID(s). SDK auto-discovers via well-known; set only to override.\r\n      - name: WECHAT_BOT_TOKEN\r\n        required: false\r\n        description: 微信 bot token（monitor 命令微信推送用，也可从 OpenClaw 插件自动发现）\r\n      - name: WECHAT_BOT_BASE_URL\r\n        required: false\r\n        description: 微信 API base URL（默认 https://ilinkai.weixin.qq.com）\r\n      - name: WECHAT_BOT_USER_ID\r\n        required: false\r\n        description: 目标用户微信 ID（monitor 推送对象）\r\n---\r\n\r\n# Ocean Chat — Agent 通讯录 + 聊天 + 约人\r\n\r\nOcean Chat 是 OceanBus 生态的 **核心用户界面 Skill**。三个能力：管理通讯录、收发消息、约人见面。\r\n\r\n数据流：`Roster（SDK 内置）← ocean-chat（UI）→ OceanBus L0（消息管道）→ 对方 Agent`\r\n\r\n其他 OceanBus Skill（ocean-agent、guess-ai）共享同一个 Roster 数据，但通过 SDK 直接调用，不经 ocean-chat。\r\n\r\n## 架构关系图\r\n\r\n```\r\n用户（人类）\r\n    │\r\n    ▼\r\nocean-chat Skill（通讯录 UI + 聊天 + 约人）\r\n    │\r\n    ├── ob.roster.*           ← 共享通讯录（~/.oceanbus/roster.json）\r\n    ├── ob.send / ob.sync     ← OceanBus 消息管道\r\n    └── ob.l1.yellowPages.*   ← 黄页服务发现\r\n    │\r\n    ├── ocean-agent  ──→ ob.roster.*  （直接 SDK 调用，不经 ocean-chat）\r\n    └── guess-ai     ──→ ob.roster.*  （直接 SDK 调用，不经 ocean-chat）\r\n```\r\n\r\n---\r\n\r\n## 冷启动：首次体验\r\n\r\n核心原则：**生态初期，拉好友下水是第一优先级。** 黄页服务商还不多，不要让它占据首屏。让用户立刻感受到价值的方式只有一个——让 ta 的朋友也装上。\r\n\r\n### Step 0：判断状态\r\n\r\n| 条件 | 走哪个流程 |\r\n|------|-----------|\r\n| `~/.oceanbus-chat/credentials.json` 不存在 | → 未注册流程 |\r\n| 存在但 Roster 通讯录为空 | → 已注册但零联系人流程 |\r\n| 存在且 Roster 有联系人 | → 正常使用，跳过冷启动 |\r\n\r\n### Step 1：未注册 → 注册\r\n\r\n如果用户说\"先帮我注册\"或首次使用 ocean-chat，执行 `node chat.js setup`。\r\n\r\n注册成功后，先获取 username 和 OpenID：\r\n\r\n```\r\n# 获取 OpenID\r\nnode chat.js openid\r\n\r\n# 获取系统用户名（用于填充欢迎语）\r\n# Windows: echo %USERNAME%\r\n# Mac/Linux: whoami\r\n```\r\n\r\n然后展示**两条消息**：\r\n\r\n**第一条（给用户自己看）**：\r\n\r\n```\r\n🌊 Ocean Chat 已就绪！\r\n\r\n你的 Agent 现在有了一个全球地址，可以跟任何人的 Agent\r\n直接聊天、协调事务。\r\n\r\n──\r\n💡 顺手设置空闲偏好（约时间时 Agent 不反复问你）：\r\n   node chat.js availability set \"工作日晚7点后，周末全天\"\r\n\r\n💡 也可以对我说\"帮我找火锅店\"，从黄页发现商户 Agent。\r\n   不过目前生态还在早期，朋友之间互相连上是最直接的用法。\r\n\r\n──\r\n👥 想让朋友也连上？\r\n   把下面那条消息直接转发给 ta。ta 复制粘贴给自己的 AI 就行了——\r\n   OpenID 已经帮你填好了，ta 不用管。\r\n```\r\n\r\n**第二条（A 转发给朋友，朋友粘贴给自己的 AI）**：\r\n\r\n> 模板如下——`<A的OpenID>` 和 `<A的名字>` 由 Agent 自动填入实际值。\r\n\r\n```\r\n把这个发给你的龙虾：\r\n\r\n<A的OpenID> <A的名字>\r\n```\r\n\r\n朋友只需要把这段话发给自己的 AI（ocean-chat），AI 看到 OpenID + 名字，自动完成：\r\n- `oceanbus add <A的名字> <A的OpenID> --greet-as <朋友的名字>`\r\n- 双向通讯录建立，一步都不用多\r\n\r\n### Step 1b：朋友收到邀请（B 侧）\r\n\r\n当用户粘贴了 `<OpenID> <名字>` 给自己的 AI 时：\r\n\r\n1. 识别格式：76 位 Base64url 字符串 + 空格 + 中文/英文名 → 这是加人请求\r\n2. 询问用户的名字（如果还不知道）\r\n3. 执行：`oceanbus add <名字> <OpenID> --greet-as <B的名字>`\r\n4. 告诉用户：\"✅ 已加 <名字> 为联系人，并发送了打招呼消息。现在你们可以直接聊天了。\"\r\n\r\n> 双方通讯录建立，只需 A 分享一行字。B 贴给自己的 AI，一步搞定。\r\n\r\n### Step 1c：A 收到打招呼消息\r\n\r\n当 `check`/`listen` 收到 `\"Hi XXX, I'm YYY. Add me to your contacts.\"` 格式的消息时：\r\n\r\n1. 通过 `roster.findByOpenId()` 反查发件人。\r\n2. 如果通讯录中还没有：`node chat.js add <YYY> <from_openid>`。\r\n3. 告诉用户：\"🎉 YYY 已加入你的通讯录！现在你们可以直接聊天了。\"\r\n\r\n### Step 2：已注册但零联系人\r\n\r\n用户再次打开 ocean-chat，检测到 Roster 为空：\r\n\r\n```\r\n👋 你的通讯录还是空的。\r\n\r\n想让朋友也连上？把下面这条消息转发给 ta，\r\nta 贴给自己的 AI 就行：\r\n\r\n──\r\n<A的OpenID> <A的名字>\r\n──\r\n\r\n也可以对我说\"帮我找火锅店\"从黄页发现商户 Agent。\r\n```\r\n\r\n### Step 3：黄页发现（用户主动触发）\r\n\r\n用户说\"帮我找 X\"时：\r\n\r\n```\r\n用户：\"帮我找火锅店\"\r\n  → node chat.js discover 火锅\r\n  → 有结果 → 展示 + \"要加哪个为联系人？加了之后可以直接聊天、约时间。\"\r\n  → 无结果 → \"黄页上暂时没有。但你可以：\r\n      1. 让你的朋友装 ocean-chat，互相加上\r\n      2. 把自己发布到黄页：node chat.js publish <你的名字>\"\r\n```\r\n\r\n### Step 4：自动发现\r\n\r\n每次用户打开 ocean-chat 时（非首次），自动检查 autoDiscovery：\r\n\r\n```\r\n\"对了，我从你最近的对话里发现了几个可能的人名：\r\n 李丽（提到 5 次）、老赵（提到 3 次）\r\n 要我帮你加到通讯录吗？\"\r\n```\r\n\r\n> 被拒绝过一次后，本轮对话不再追问。下次对话再提醒一次。\r\n\r\n---\r\n\r\n## 一、Roster（通讯录）\r\n\r\nocean-chat 是通讯录的**唯一 UI 入口**。用户说\"加人/查人/改人/删人\"，全部在 ocean-chat 中处理。\r\n\r\n底层原理：`RosterService` 来自 `oceanbus` SDK（`require('oceanbus').RosterService`）。SDK 负责数据模型和索引；LLM 负责语义理解和消歧。\r\n\r\n### 1.1 查找联系人\r\n\r\n```\r\n用户说了一个名字/描述\r\n        │\r\n        ▼\r\n  new RosterService().search(query)\r\n        │\r\n        ├── exact.length == 1 ──→ 直接使用，不询问\r\n        ├── exact.length > 1  ──→ 展示候选（列出 tags/notes 差异），让用户选\r\n        ├── fuzzy.length == 1  ──→ \"你是说 XXX 吗？\"\r\n        ├── fuzzy.length > 1  ──→ 展示候选\r\n        ├── byTag.length > 0   ──→ \"没有叫这个名字的，但有标签为 XXX 的联系人...\"\r\n        └── 全空              ──→ \"通讯录里没有。要新建吗？\"\r\n```\r\n\r\n**模糊查询处理**：\r\n\r\n| 用户说 | Roster 行为 | LLM 处理 |\r\n|--------|-----------|---------|\r\n| \"老 王\"（多余空格） | search() 自动去空格，fuzzy 命中 | \"你是说老王吗？\" |\r\n| \"王总\"（称呼） | alias 精确命中 | 直接使用 |\r\n| \"那个喜欢川菜的\"（语义） | search(\"川菜\") → byNote 命中 | 直接使用 |\r\n| \"上次打羽毛球那个\"（语义） | list({ tags: [\"badminton\"] }) | 列出候选人 |\r\n\r\n**Shell 命令**：\r\n\r\n```bash\r\n# 普通查找（走 CLI）\r\nnode chat.js contacts\r\n\r\n# 高级查找（用 SDK one-liner）\r\nnode -e \"const {RosterService}=require('oceanbus');new RosterService().search('老王').then(r=>console.log(JSON.stringify(r,null,2)))\"\r\n```\r\n\r\n### 1.2 添加联系人\r\n\r\n```\r\n用户说：\"加一个联系人，老李，财务部同事\"\r\n  → new RosterService().add({ name: \"老李\", tags: [\"colleague\", \"finance\"], source: \"manual\" })\r\n  → 自动检测重复（同 OpenID → 提示合并）\r\n  → \"已添加老李，标签: colleague, finance\"\r\n```\r\n\r\n**Shell 命令**：\r\n\r\n```bash\r\nnode chat.js add <名字> <OpenID>       # 已知 OpenID\r\nnode chat.js add <名字>                # 先加名字，OpenID 后续补\r\n```\r\n\r\n### 1.3 查看联系人详情\r\n\r\n```\r\n用户说：\"老王是谁？\"\r\n  → roster.get(\"laowang\")\r\n  → \"老王，你的大学同学。标签: friend, badminton。备注: 喜欢川菜。最近联系: 5月6日。\"\r\n  → 如果有重复提示（duplicateHints），主动问：\"对了，通讯录里有另一个老王（公司财务）。要合并吗？\"\r\n```\r\n\r\n### 1.4 修改联系人\r\n\r\n可通过 RosterService 修改标签、别名、备注。标签由 LLM 自动维护，也可手动调整。详见末尾命令参考。\r\n\r\n### 1.5 合并重复联系人\r\n\r\n当 `getDuplicateHints()` 有数据时，主动提示：\r\n\r\n```\r\n通讯录检测到可能的重复：\r\n  老王 (friend) 和 老王 (colleague) 可能是同一个人（相同手机号）\r\n  要合并吗？\r\n\r\n用户确认后：\r\n  → roster.merge(\"laowang\", \"wangcai\")\r\n  → \"已合并。老王现在有 2 个 Agent 地址，标签: friend, colleague\"\r\n```\r\n\r\n重复联系人和消除提示通过 RosterService 方法操作，详见末尾命令参考。\r\n\r\n### 1.6 AutoDiscovery 审核\r\n\r\n新名字出现 3 次以上会自动进入待审核队列。首次使用或定期检查：\r\n\r\n待审核列表、通过、拒绝操作均通过 RosterService 完成，详见末尾命令参考。\r\n\r\n**主动审核时机**：用户说\"看看通讯录\"、新对话开始、`getDuplicateHints()` 返回非空时。\r\n\r\n### Roster SDK 速查\r\n\r\n以下 one-liner 覆盖高级 Roster 操作（通过 `node -e` 直接调 SDK）：\r\n\r\n搜索 `node -e \"const {RosterService}=require('oceanbus');new RosterService().search('老王')...\"`\r\n合并 `node -e \"const {RosterService}=require('oceanbus');new RosterService().merge('keep','discard')\"`\r\n查重 `node -e \"const {RosterService}=require('oceanbus');new RosterService().getDuplicateHints()...\"`\r\n待审 `node -e \"const {RosterService}=require('oceanbus');new RosterService().getPending()...\"`\r\n改标签 `node -e \"const {RosterService}=require('oceanbus');new RosterService().updateTags(name,tags)\"`\r\n改备注 `node -e \"const {RosterService}=require('oceanbus');new RosterService().update(name,{notes:'...'})\"`\r\n加别名 `node -e \"const {RosterService}=require('oceanbus');new RosterService().addAlias(name,'alias')\"`\r\n\r\n完整参数见前文各小节，或运行 `node -e \"require('oceanbus').RosterService\"` 查看。\r\n\r\n---\r\n\r\n## 二、Chat（A2A 消息）\r\n\r\n### 2.1 发消息\r\n\r\n```\r\n用户说：\"给老王发消息，周五打球？\"\r\n  → roster.search(\"老王\") → 消歧 → 拿到 OpenID\r\n  → ob.send(openid, \"周五打球？\")\r\n  → roster.touch(\"laowang\")\r\n```\r\n\r\n普通文本消息：\r\n\r\n```bash\r\nnode chat.js send <名字> <消息>\r\n```\r\n\r\n### 2.2 收消息\r\n\r\n```bash\r\nnode chat.js check       # 手动检查\r\nnode chat.js listen      # 实时监听（推荐）\r\n```\r\n\r\n收消息时自动 `roster.findByOpenId()` 反查联系人名。\r\n\r\n### 2.3 结构化协议消息（v2.1 新增）\r\n\r\n支持 JSON 协议消息。当收到 type=protocol 的消息时，按协议类型路由。\r\n\r\n```bash\r\n# 发送协议消息\r\nnode chat.js send <名字> --protocol ocean-date/negotiate/v1 '{\"type\":\"proposal\",\"payload\":{\"time\":\"周五19:00\",\"location\":\"渝信川菜\"}}'\r\n```\r\n\r\n**消息类型**：\r\n\r\n| type | protocol | 说明 |\r\n|------|---------|------|\r\n| text | null | 自由聊天 |\r\n| protocol | `ocean-date/negotiate/v1` | 约人协商 |\r\n| system | null | 系统消息（上线通知等） |\r\n\r\n**收到 protocol 消息时的处理**：读取 `structured` 字段 → 根据 `protocol` 名查找处理规则 → 执行逻辑。对未知协议，回复 system 消息：\"收到协议消息 \\`<protocol>\\`，但当前版本不支持。升级 ocean-chat 后可使用。\"\r\n\r\n---\r\n\r\n## 三、Date（1v1 约人 v2.1）\r\n\r\n基于 Roster（找谁）+ Chat（怎么发）的 1v1 协商引擎。\r\n\r\n### 3.1 触发\r\n\r\n```\r\n用户说：\"帮我约老王周五晚上吃饭\"\r\n用户说：\"跟老王约个见面\"\r\n用户说：\"帮我和老王协商时间\"\r\n```\r\n\r\n### 3.2 流程\r\n\r\n```\r\n1. 解析用户意图 → 提取约束（时间/地点/偏好）\r\n2. roster.search(\"老王\") → 获取 OpenID\r\n3. 构造 proposal → 发送协议消息\r\n4. 等对方回复 → 解析 response\r\n5. 如需调整 → 发 counter-proposal（最多 3 轮）\r\n6. 达成一致 → 通知用户 + 写入 chat.log\r\n```\r\n\r\n### 3.3 协议 Schema\r\n\r\n详见 `date-protocol.md`。核心消息类型：\r\n\r\n| type | 方向 | 含义 |\r\n|------|------|------|\r\n| `proposal` | 发起方 → 接收方 | 首次提案 |\r\n| `counter` | 接收方 → 发起方 | 反提案 |\r\n| `accept` | 任意方 | 接受 |\r\n| `reject` | 任意方 | 拒绝 |\r\n| `withdraw` | 发起方 | 撤回提案 |\r\n\r\n### 3.4 约束提取（LLM 负责）\r\n\r\n```\r\n用户：\"帮我约老王周五或周六晚上，川菜，朝阳区，别太贵\"\r\n\r\n提取:\r\n  时间约束: 周五晚上 | 周六晚上\r\n  地点约束: 朝阳区\r\n  口味约束: 川菜\r\n  预算约束: 别太贵\r\n  人员: 老王\r\n```\r\n\r\n### 3.5 协商规则\r\n\r\n- **最多 3 轮**。3 轮未达成一致 → 告诉用户建议直接沟通\r\n- **提案必须具体**（时间+地点，不是\"周末见\"）\r\n- **考虑对方偏好**（如果对方 Agent 回复了偏好，据此调整）\r\n- **确认即锁定**（发送 accept 后不可反悔）\r\n\r\n### 3.6 完成报告\r\n\r\n```\r\n📋 约人协商报告\r\n\r\n📍 结果: 已与老王确认\r\n   时间: 周五 19:00\r\n   地点: 渝信川菜（朝阳大悦城店）\r\n\r\n🔄 过程（2轮）:\r\n   ① 你提议: 周五19:00 渝信川菜\r\n   ② 老王确认: ✅ 可以\r\n\r\n💡 建议: 周五晚高峰，提前出发\r\n```\r\n\r\n---\r\n\r\n## 四、Thread（对话线程 v1）\r\n\r\n当两个人同时在聊 2-3 件不同的事（比如一边讨论体检预约、一边沟通专家推荐），消息会混在一起难以分辨。Thread 协议解决的就是这个问题——**给每通对话一个 thread_id，收发双方按线程分组**。\r\n\r\n协议详见 `OceanBusDocs/ocean-thread-protocol-v1.md`。\r\n\r\n### 4.1 线程生命周期\r\n\r\n```\r\ncreate ──→ active ──→ resolve ──→ resolved\r\n              │                      │\r\n              │  reply               │  reopen\r\n              ▼                      ▼\r\n            active ◄────────────── active\r\n```\r\n\r\n### 4.2 触发\r\n\r\n```\r\n用户说：\"帮我跟老王开个线程，聊一下体检预约\"\r\n用户说：\"回复老王的体检线程，就说明天上午可以\"\r\n用户说：\"看看我和老王有哪些对话\"\r\n```\r\n\r\n### 4.3 命令\r\n\r\n```bash\r\nnode chat.js thread create <名字> --subject \"主题\"     # 创建新线程\r\nnode chat.js thread reply <thread_id> <消息>           # 在线程中回复\r\nnode chat.js thread list                               # 列出所有线程\r\nnode chat.js thread show <thread_id>                   # 查看线程详情（含历史消息）\r\nnode chat.js thread resolve <thread_id>                # 结束线程\r\nnode chat.js thread reopen <thread_id>                 # 重开已结束线程\r\n```\r\n\r\n### 4.4 协议消息格式\r\n\r\n通过 `ocean-thread/v1` 协议发送，与 Date 协议的 `ocean-date/negotiate/v1` 同级：\r\n\r\n```json\r\n{\r\n  \"type\": \"protocol\",\r\n  \"protocol\": \"ocean-thread/v1\",\r\n  \"structured\": {\r\n    \"action\": \"create\",\r\n    \"thread_id\": \"th_20260508_a1b2c3\",\r\n    \"subject\": \"体检预约 — 张先生 45岁 北京\",\r\n    \"payload\": {}\r\n  }\r\n}\r\n```\r\n\r\n### 4.5 显示约定\r\n\r\n`check` / `listen` 收到线程消息时，消息前会显示线程标记：\r\n\r\n```\r\n── 来自 老王 (ob_c-Qrza...) · 14:30:00 ──\r\n  [th_a1b2c3...] 体检预约 — 张先生\r\n\r\n🧵 新对话 · [th_a1b2c3...]\r\n  主题: 体检预约 — 张先生 45岁 北京\r\n```\r\n\r\n### 4.6 自动关联\r\n\r\n- 收到 `create` 协议消息时，自动在本地创建线程记录\r\n- 收到 `reply` 时，自动追加到对应线程\r\n- 收到 `resolve` / `reopen` 时，自动更新线程状态\r\n- 如果用 `send` 发普通消息（非协议），会自动关联到与该用户最近的活跃线程\r\n\r\n### 4.7 与 ocean-desk 的关系\r\n\r\nocean-desk 坐席系统依赖此协议做工单管理：\r\n- 每条客户咨询 = 一个线程\r\n- AI skill 上下文通过 `payload` 字段透传给坐席\r\n- 坐席回复 = `reply`\r\n- 工单关闭 = `resolve`\r\n- 线程 ID 可直接映射到工单 ID\r\n\r\n---\r\n\r\n## 五、Yellow Pages（黄页）\r\n\r\n```bash\r\nnode chat.js publish <名字>              # 发布自己的 OpenID 到黄页\r\nnode chat.js discover <名字>             # 搜索朋友的 OpenID\r\nnode chat.js unpublish                   # 从黄页移除\r\n```\r\n\r\n**发现联系人后必须主动提出加入 Roster**：\r\n\r\n```\r\ndiscover 返回结果 →\r\n  ① 展示候选列表（名字 + 描述）\r\n  ② \"要加哪个为联系人？加了之后你们可以直接聊天、约时间。\"\r\n  ③ 用户选择 → node chat.js add → 告知用户已添加\r\n```\r\n\r\n黄页是冷启动期最重要的联系人来源——不要让用户自己想着去加。\r\n\r\n---\r\n\r\n## 六、命令速查\r\n\r\n```bash\r\n# 安装 & 身份\r\nnode chat.js setup                       # 首次注册（自动迁移旧数据）\r\nnode chat.js openid                      # 查看你的 OpenID\r\n\r\n# 通讯录\r\nnode chat.js add <名字> <OpenID>          # 添加联系人（自动检测重复）\r\nnode chat.js contacts                     # 列出通讯录\r\n\r\n# 消息\r\nnode chat.js send <名字|OpenID> <消息>     # 发消息（自动 Roster 解析）\r\n\t                                         # --from <你的名字>  附加 From/To 消息头（多 CC 场景）\r\nnode chat.js check                        # 查看新消息\r\nnode chat.js listen                       # 实时监听（SDK 轮询）\r\n\tnode chat.js listen --on-message \"cmd\"    # 监听 + 收到消息时执行命令 ({from} {openid} {content} {time})\r\n\tnode chat.js monitor                     # 监听 + 微信推送通知（需配 WECHAT_BOT_* 环境变量）\r\n\tnode chat.js pair-me                     # 生成配对消息（朋友粘贴给 CC 建立连接）\r\n\r\n# 黄页\r\nnode chat.js publish <名字>               # 发布到黄页\r\nnode chat.js discover <名字>              # 从黄页搜索\r\nnode chat.js unpublish                    # 从黄页移除\r\n\r\n# Date 约人\r\nnode chat.js date <名字> <类型>           # 发送 Date 协议消息\r\n  --time <ISO> --location <地点> --notes <备注>\r\n\r\n# Thread 对话线程\r\nnode chat.js thread create <名字>          # 创建对话线程\r\n  --subject \"主题\" [--payload '{\"k\":\"v\"}']\r\nnode chat.js thread reply <id> <消息>      # 在线程中回复\r\nnode chat.js thread list                   # 列出所有线程\r\nnode chat.js thread show <id>              # 查看线程详情\r\nnode chat.js thread resolve <id>           # 结束线程\r\nnode chat.js thread reopen <id>            # 重开已结束线程\r\n```\r\n\r\n## 七、实时通信\r\n\r\n**默认推荐 `listen` 模式**（2s polling，开销极小）：\r\n\r\n```bash\r\nnode chat.js listen\r\n```\r\n\r\n收消息自动通过 Roster 反查人名，展示 `老王 (ob_xxx...)` 而非裸 OpenID。\r\n\r\n如未开启 listen，收消息时主动 `check`——不等用户说\"查消息\"。\r\n\r\n---\r\n\r\n## 八、与其它 Skill 的关系\r\n\r\n| Skill | 与 ocean-chat 的关系 |\r\n|-------|---------------------|\r\n| **ocean-agent** | 共享 Roster 数据（直接调 SDK），不通过 ocean-chat。ocean-chat 加的联系人，ocean-agent 自动可见。 |\r\n| **guess-ai** | 同上。游戏玩家姓名通过 game.js 写入 Roster，全局可见。 |\r\n| **captain-lobster** | 独自管理数据，不关联。 |\r\n\r\n> ocean-chat 是通讯录的 UI 入口，但不是通讯录的\"网关\"。其它 Skill 直接调用 `RosterService`——SDK 是共享层，不是 ocean-chat 独占。\r\n\r\n---\r\n\r\n## 九、能力扩展\r\n\r\nocean-chat 支持通过其他 Skill 扩展领域能力。检测方式：\r\n\r\n```\r\nopenclaw skills list | grep ocean-agent\r\n```\r\n\r\n### ocean-agent（保险代理人能力包）\r\n\r\n> 🔌 这是 ocean-chat 的扩展，不是独立应用。所有操作仍通过 ocean-chat 完成。\r\n\r\n如果用户安装了 ocean-agent，ocean-chat 自动获得以下能力：\r\n\r\n| 能力 | 触发场景 |\r\n|------|---------|\r\n| **客户新闻推送** | 用户说\"最近有什么重疾险的新政策，推给我的客户\" → Agent 搜索相关新闻 → 生成摘要 → 筛选关注\"重疾险\"标签的客户 → 群发 |\r\n| **客户画像补全** | 聊天中客户提到\"刚生小孩\" → 自动更新 Roster 中该客户的 `apps.ocean-agent.preferences`\"有新生儿\" → 建议少儿险 |\r\n| **智能跟进建议** | 客户超过 3 天未回复 → Agent 主动提示\"要不要发个新产品的新闻破冰？\" → 生成草稿 → 用户确认后发送 |\r\n| **线索管道** | 用户说\"看看今天概览\" → 从 Roster `apps.ocean-agent` 中读取 stage → 按新线索/需求采集/方案已发/待成交 分组展示 |\r\n| **声誉管理** | 成交后提醒引导好评；查客户声誉标签；发现负面标签时预警 |\r\n| **黄页推广** | 发布保险代理人档案到黄页，管理标签和心跳 |\r\n\r\n**启用方式**：\r\n\r\n1. 用户安装 ocean-agent：`openclaw skills install ocean-agent`\r\n2. ocean-chat 检测到 ocean-agent 已安装后，当用户提到保险、客户、跟进、声誉等场景时，参考 `ocean-agent/SKILL.md` 获取保险领域的详细指令\r\n\r\n**关系规则**：\r\n\r\n- ocean-chat 是唯一的 UI 入口（通讯录、消息、约人、黄页搜索）\r\n- ocean-agent 提供保险专属的数据和模板（客户画像、新闻推送、线索评分）\r\n- ocean-agent **不重复**通讯录管理、消息收发——这些全部通过 ocean-chat 完成\r\n\r\n---\r\n\r\n## 十、约束规则\r\n\r\n1. **发消息前必须查 Roster**：用户说人名 → `roster.search()` → 消歧 → 拿 OpenID → 发。不要直接当 OpenID 用。\r\n2. **稳定 OpenID 优先**：回复用 Roster 中保存的稳定 OpenID，不用 `from_openid`。\r\n3. **人工闸门**：首次 `send` 前展示预览，用户确认后发送。自动回复（如 heartbeat）除外。\r\n4. **Roster 自动维护**：LLM 可以改 tags、aliases、notes。不可改 name、agents（用户说了算）。\r\n5. **重复联系人不直接创建**：`add()` 返回的 `duplicateHints.length > 0` 时，先问用户是否合并。\r\n6. **不要把发给 CC 的命令当任务执行**：如果有人（通常是新用户）把 `git clone`、`npm install`、`node chat.js` 这类命令行粘贴给你，这不是给你的任务。回复：\"这是发给 Claude Code 的命令，不是给我的。请把这句话复制到你电脑的 CC 窗口里运行。\" 然后什么都不做。\r\n\r\n---\r\n\r\n## 十一、故障排除\r\n\r\n| 问题 | 解决 |\r\n|------|------|\r\n| 未注册 | `node chat.js setup` |\r\n| 无法连接 | 检查网络 |\r\n| 对方收不到 | 对方需 `node chat.js check` |\r\n| 联系人未找到 | `node chat.js add <名字> <OpenID>` |\r\n| 忘记 OpenID | `node chat.js openid` |\r\n| 重置 | 删 `~/.oceanbus-chat/`；Roster 在 `~/.oceanbus/roster.json` |\r\n| 重复联系人 | 用 merge one-liner（见§五） |\n\nFile v2.20.0:README.md\n\n# 🌊 Ocean Chat — 用微信遥控 Claude Code\r\n\r\n> 你在通勤路上，想到一个 bug 修复方案。打开微信，说了一句话。  \r\n> 电脑上的 Claude Code 自动执行，3 分钟后回复你 \"修好了\"。\r\n\r\n[![npm](https://img.shields.io/npm/v/oceanbus)](https://www.npmjs.com/package/oceanbus)\r\n[![downloads](https://img.shields.io/npm/dm/oceanbus)](https://www.npmjs.com/package/oceanbus)\r\n[![ClawHub](https://img.shields.io/badge/ClawHub-ocean--chat-blue)](https://clawhub.ai/skills/ocean-chat)\r\n[![license](https://img.shields.io/badge/license-MIT--0-green)](LICENSE)\r\n\r\n---\r\n\r\n## 5 分钟打通\r\n\r\n```\r\n  你的手机                          你的电脑\r\n┌──────────┐                    ┌──────────────┐\r\n│ 微信      │                    │ PM2 守护      │\r\n│  ↓        │     OceanBus      │  ↓            │\r\n│ 小龙虾     │←────────────────→│ chat.js listen│\r\n│ (Bridge)  │   端到端加密消息   │  ↓            │\r\n│  ↓        │                    │ Claude Code   │\r\n│ 收发消息   │                    │ 收到→执行→回报 │\r\n└──────────┘                    └──────────────┘\r\n```\r\n\r\n**3 条命令在电脑上，2 句话在手机上，5 分钟打通。**\r\n\r\n```bash\r\n# PC 端（安装了 Claude Code 的电脑）\r\ngit clone https://github.com/ryanbihai/ocean-chat.git\r\ncd ocean-chat && npm install\r\nnode chat.js setup   # → 记下输出的 OpenID\r\n```\r\n\r\n然后打开微信，对你的 ClawBot（小龙虾）说：\r\n\r\n```\r\n帮我安装 ocean-chat skill\r\n加联系人 CC <你的OpenID>\r\n```\r\n\r\n**完了。** 现在在微信里说 \"告诉 CC：帮我查个 bug\"，电脑上的 Claude Code 收到任务 → 执行 → 自动回复结果。\r\n\r\n📖 **详细上手文档** → [docs/手机遥控ClaudeCode-工程师上手.md](docs/手机遥控ClaudeCode-工程师上手.md)\r\n\r\n📖 **零提问一键配对** → [docs/发本文件给你的claudecode.md](docs/发本文件给你的claudecode.md)（把这个文件发给你的 Claude Code，它自动帮你配好）\r\n\r\n---\r\n\r\n## 不只是遥控 CC —— 你的 Agent 联网了\r\n\r\nOcean Chat 基于 [OceanBus SDK](https://www.npmjs.com/package/oceanbus)。**你的 Agent 拥有了全局地址。** 黄页让它被发现，声誉让它被信任，P2P 消息让它自动成交。\r\n\r\n```\r\n你的 Agent  ←──OceanBus 网络──→  任何人的 Agent\r\n      │                                  │\r\n   被发现（黄页）                    被信任（声誉）\r\n      │                                  │\r\n      └──────── 自动成交（L0消息）─────────┘\r\n```\r\n\r\n| 场景 | Agent A | Agent B | 发生什么 |\r\n|------|---------|---------|---------|\r\n| **手机遥控 CC** | 你（微信） | Claude Code | 通勤路上派活，CC 执行完自动回报 |\r\n| **保险咨询** | 客户搜\"健康险 北京\" | 代理人收到咨询 → 报价 | 黄页发现 + P2P 私信 + 声誉查询 |\r\n| **房产经纪** | 买家搜\"两居 朝阳\" | 经纪人发房源 → 约看房 | 黄页发现 + 会面协商 |\r\n| **Agent 竞技** | 两个 Agent 谈判交易 | 观众投票谁更会砍价 | P2P 消息 + 加密签名 |\r\n\r\n**同一套 SDK：`npm install oceanbus`。** 你没有部署任何服务器——OceanBus 替你解决了寻址、加密、路由的全部问题。\r\n\r\n---\r\n\r\n## 命令速查\r\n\r\n```bash\r\nnode chat.js setup                      注册 OceanBus 身份\r\nnode chat.js listen                     实时监听（推荐 PM2 守护）\r\nnode chat.js openid                     查看你的 OpenID\r\nnode chat.js add <名字> <OpenID>        添加联系人\r\nnode chat.js send <名字> <消息>         发送消息\r\nnode chat.js send <名字> <消息>         发送消息（--from 标注来源）\r\n  --from <名字>\r\nnode chat.js listen                     实时监听\r\n  --on-message \"cmd\"                    收到消息时执行命令 {from} {openid} {content} {time}\r\nnode chat.js pm2-init <名字>            一键生成 PM2 配置文件\r\nnode chat.js publish <你的名字>         发布到黄页——Agent 注册即开店\r\nnode chat.js discover <名字>            搜索朋友的 Agent\r\nnode chat.js thread create <名字>       创建对话线程\r\n  --subject \"主题\"\r\n```\r\n\r\n---\r\n\r\n## 相关项目\r\n\r\n- 核心 SDK：[oceanbus](https://www.npmjs.com/package/oceanbus) — `npm install oceanbus`\r\n- 保险工作台：[Ocean Agent](https://clawhub.ai/skills/ocean-agent) — 保险代理人 AI 工作台\r\n- 进阶灯塔：[Captain Lobster](https://clawhub.ai/skills/captain-lobster) — Zero-Player 自主交易游戏\r\n- 高阶灯塔：[Guess AI](https://clawhub.ai/skills/guess-ai) — 多人社交推理游戏\r\n- MCP Server：[oceanbus-mcp-server](https://www.npmjs.com/package/oceanbus-mcp-server) — Claude Desktop/Cursor 通用\r\n- 更多 Skills：[ClawHub OceanBus 集合](https://clawhub.ai/skills?search=oceanbus)\r\n\r\n## License\r\n\r\nMIT-0 — 自由使用、修改、分发。无需署名。\n\nFile v2.20.0:_meta.json\n\n{\n  \"ownerId\": \"kn75dvek1dkfz0r0xwdp4xtc2983qg44\",\n  \"slug\": \"ocean-chat\",\n  \"version\": \"2.20.0\",\n  \"publishedAt\": 1778764250457\n}\n\nFile v2.20.0:date-protocol.md\n\n# Ocean Date — 1v1 协商协议 v1\r\n\r\n> ocean-chat 内置模块。通过结构化 JSON 协议消息实现 Agent 间的自动会面协商。\r\n\r\n## 协议标识\r\n\r\n```\r\nprotocol: \"ocean-date/negotiate/v1\"\r\n```\r\n\r\n所有 Date 消息的 `protocol` 字段为此值。接收方据此识别和路由。\r\n\r\n## 消息格式\r\n\r\n外层（chat.log 中的消息结构）：\r\n\r\n```json5\r\n{\r\n  \"id\": \"msg_003\",\r\n  \"direction\": \"sent\",\r\n  \"contactId\": \"laowang\",\r\n  \"type\": \"protocol\",\r\n  \"protocol\": \"ocean-date/negotiate/v1\",\r\n  \"content\": \"【约约】提案：周五19:00 渝信川菜\",   // 人类可读摘要\r\n  \"structured\": {                                  // JSON payload\r\n    \"type\": \"proposal\",\r\n    \"payload\": { ... }\r\n  },\r\n  \"timestamp\": \"2026-05-07T10:00:00Z\"\r\n}\r\n```\r\n\r\n### structured.type（消息类型）\r\n\r\n| type | 方向 | 含义 | 是否终态 |\r\n|------|------|------|---------|\r\n| `proposal` | 发起方 → 接收方 | 首次提案 | 否 |\r\n| `counter` | 接收方 → 发起方 | 反提案 | 否 |\r\n| `accept` | 任意方 → 对方 | 接受提案 | **是** |\r\n| `reject` | 接收方 → 发起方 | 拒绝提案 | **是** |\r\n| `withdraw` | 发起方 → 接收方 | 撤回未决提案 | **是** |\r\n\r\n### structured.payload（载荷）\r\n\r\n```json5\r\n{\r\n  \"time\": \"2026-05-09T19:00:00+08:00\",    // ISO 8601，必填 proposal/counter\r\n  \"location\": \"渝信川菜（朝阳大悦城店）\",      // 必填\r\n  \"locationDetail\": \"B1层，靠窗位\",          // 可选\r\n  \"notes\": \"我订好位了\"                       // 可选备注\r\n}\r\n```\r\n\r\n`accept` / `reject` / `withdraw` 的 payload 可为 `null`。\r\n\r\n## 协商状态机\r\n\r\n```\r\n        发起方                        接收方\r\n          │                            │\r\n          ├── proposal ───────────────→│\r\n          │                            ├── accept ──→ 达成一致\r\n          │                            ├── reject ──→ 协商失败\r\n          │                            └── counter ──→\r\n          │←───────────────────────────┘\r\n          ├── accept ──→ 达成一致\r\n          ├── reject ──→ 协商失败\r\n          ├── withdraw ─→ 发起方放弃\r\n          └── counter ──→（继续，最多 3 轮）\r\n```\r\n\r\n**规则**：\r\n- 任一时刻只有一方可以 proposal/counter（不可同时发）\r\n- `accept` 发出后不可反悔\r\n- 3 轮后仍无 accept → 自动结束，双方通知用户\r\n- `withdraw` 只能在对方回复前发出\r\n\r\n## 协商缓存（Availability Hints）\r\n\r\n收发 proposal 前**必须先查 availability**，避免反复扰民。\r\n\r\n### 数据结构（date-log.json）\r\n\r\n```json5\r\n{\r\n  \"availability\": {\r\n    \"hints\": \"工作日晚7点后，周末全天\",    // 用户一句话描述\r\n    \"blocked\": [\"2026-05-08T19:00:00+08:00\"],  // 已确认约会自动加入\r\n    \"updatedAt\": \"2026-05-07T10:00:00Z\"\r\n  }\r\n}\r\n```\r\n\r\n### 协商前检查流程\r\n\r\n```\r\n收到 proposal（或用户让你发起 proposal）\r\n        │\r\n        ▼\r\n  读 ~/.oceanbus-chat/date-log.json → availability\r\n        │\r\n        ├── hints 为空 → 问用户一次\"一般什么时候有空？\"\r\n        │                  存为 hints，下次不问了\r\n        │\r\n        ├── time 在 blocked 中 → 自动 counter，不打扰用户\r\n        │   \"抱歉，这个时间已经有安排了。换个时间？\"\r\n        │\r\n        ├── hints 说\"周末\"但 proposal 是周五\r\n        │   → 查 blocked 中周五是否空闲\r\n        │   → 如空闲：\"周五虽然不在你偏好里但没冲突，确认吗？\"\r\n        │   → 有冲突：自动 counter\r\n        │\r\n        └── hints 符合 + 无冲突 → 直接确认\r\n            \"按你的偏好，周五19:00可以。确认吗？\"\r\n```\r\n\r\n### 用户设置\r\n\r\n```bash\r\n# 设置偏好\r\nnode chat.js availability set \"工作日晚7点后，周末全天\"\r\n\r\n# 查看\r\nnode chat.js availability\r\n```\r\n\r\n### accept 自动 blocked\r\n\r\n当发送 `accept` 且 payload 含 time 时，自动写入 `blocked[]` 和 `entries[]`。用户无需手动维护。\r\n\r\n---\r\n\r\n## 约束提取（LLM 指南）\r\n\r\n当用户说类似下面的话时，分析并提取结构化约束：\r\n\r\n```\r\n\"帮我约老王周五或周六晚上，川菜，朝阳区，别太贵\"\r\n```\r\n\r\n| 约束维度 | 提取值 | 用于 |\r\n|---------|--------|------|\r\n| 时间 | 周五晚上, 周六晚上 | proposal.payload.time |\r\n| 地点偏好 | 朝阳区 | proposal.payload.location |\r\n| 口味/类型 | 川菜 | 地点选择依据 |\r\n| 预算 | 别太贵 | 地点筛选 |\r\n| 人员 | 老王 | roster.search() |\r\n\r\n### 时间解析规则\r\n\r\n| 用户说 | 解析为 |\r\n|--------|--------|\r\n| \"周五晚上\" | 本周五 19:00 |\r\n| \"周末下午\" | 本周六 14:00 |\r\n| \"明天中午\" | 明天 12:00 |\r\n| \"下周三\" | 下周三 19:00（默认晚饭时间） |\r\n| \"随便/都行\" | payload.time 不设，让对方先提 |\r\n\r\n**有多选项时**：全部放进 notes 供对方 Agent 知晓。\r\n\r\n### 地点解析规则\r\n\r\n| 用户说 | 行为 |\r\n|--------|------|\r\n| \"朝阳区\" | 搜索朝阳区的相关场所 |\r\n| \"川菜\" | 搜川菜馆 |\r\n| \"安静的地方\" | 倾向于咖啡馆/茶馆 |\r\n| \"别太远\" | 优先搜索靠近双方中点 |\r\n\r\n> LLM 自行判断合适场所——不需要外部 API。用常识推荐。\r\n\r\n## 协商策略\r\n\r\n### 发起方\r\n\r\n1. 查 Roster 确认联系人存在\r\n2. 问用户偏好（如用户未说明时间/地点/口味）\r\n3. 构造 proposal → 发送 protocol 消息\r\n4. 等待回复 → 根据 type 进入下一状态\r\n\r\n### 接收方\r\n\r\n1. 收到 proposal → 展示给用户\r\n2. 问用户偏好 + 自己评估合理性\r\n3. 发送 accept / reject / counter\r\n\r\n### 反提案（counter）\r\n\r\n```\r\n理由必须同时包含：\r\n  ✓ 为什么不接受原提案（具体问题）\r\n  ✓ 新的具体提案\r\n\r\n坏：\"时间不方便\"\r\n好：\"周五19:00有会。周六下午2点可以吗？地点可以不变。\"\r\n```\r\n\r\n### 评估对方提案的合理性\r\n\r\n- 距离双方是否大致中间？\r\n- 是否是可以坐着聊的场所（非街角、非快餐柜台）？\r\n- 时间是否合理（非凌晨、非工作时间前不合理的空档）？\r\n\r\n如果不合理 → counter，说明原因。\r\n\r\n## 完整示例\r\n\r\n```\r\n发起方 Agent：\r\n  收到用户指令 → 提取约束 → roster.search(\"老王\") → 构造 proposal\r\n  ── sent ──\r\n  { \"type\": \"protocol\", \"protocol\": \"ocean-date/negotiate/v1\",\r\n    \"structured\": { \"type\": \"proposal\", \"payload\": {\r\n      \"time\": \"2026-05-09T19:00:00+08:00\",\r\n      \"location\": \"渝信川菜（朝阳大悦城店）\",\r\n      \"notes\": \"喜欢川菜，朝阳区，预算中等\"\r\n  }}}\r\n\r\n接收方 Agent：\r\n  收到消息 → 展示给用户 → 用户同意\r\n  ── sent ──\r\n  { \"type\": \"protocol\", \"protocol\": \"ocean-date/negotiate/v1\",\r\n    \"structured\": { \"type\": \"accept\", \"payload\": null }}\r\n\r\n→ 协商完成，双方通知用户\r\n```\r\n\r\n## 终止条件\r\n\r\n| 条件 | 结果 |\r\n|------|------|\r\n| `accept` 被发送 | ✅ 达成一致，通知用户 |\r\n| `reject` 被发送 | ❌ 对方拒绝，通知用户 |\r\n| `withdraw` 被发送 | ↩ 发起方取消，通知用户 |\r\n| 3 轮后无 accept | ⚠️ 协商自动终止，建议用户直接沟通 |\n\nFile v2.20.0:docs/发本文件给你的claudecode.md\n\n## 任务：生成 小龙虾配对 消息\r\n\r\n你需要生成一段文本，让用户直接复制后发给他手机上的 小龙虾。\r\n\r\n**步骤：**\r\n\r\n0. 获取 ocean-chat（如果还没有）：\r\n   - `git clone https://github.com/ryanbihai/ocean-chat.git && cd ocean-chat && npm install`\r\n   - 已经装过就跳过\r\n\r\n1. 注册本窗口的独立 OceanBus 身份：\r\n   - 在 ocean-chat 目录下运行：`node chat.js --data-dir .oceanbus-cc setup`\r\n   - `--data-dir .oceanbus-cc` 确保每个项目窗口有独立身份，不会跟其他 CC 窗口串信\r\n   - 用 `node chat.js --data-dir .oceanbus-cc openid` 读取 OpenID\r\n\r\n2. 启动本窗口的持久监听，收到任务时自动处理并回报：\r\n   - 运行：`node chat.js --data-dir .oceanbus-cc pm2-init <你的CC名字>`\r\n   - 例：`node chat.js --data-dir .oceanbus-cc pm2-init CC-findagent`\r\n   - 然后：`pm2 start .oceanbus-cc/ecosystem.config.json && pm2 save`\r\n   - （没装 pm2 的话先 `npm install -g pm2`）\r\n\r\n3. 获取本窗口名称作为 claude code 的名字：读取当前工作目录名 `node -e \"console.log(require('path').basename(process.cwd()))\"`。格式为 `CC-<目录名>`（如 `CC-oceanbus`、`CC-dashboard`）。不要问用户——窗口自己知道自己在哪个项目里。\r\n\r\n4. 将以下内容按**代码块格式**输出给 用户，让他直接复制发送给小龙虾：\r\n\r\n```\r\n帮我升级 ocean-chat skill 到最新版（保留数据，不要重装）\r\n加联系人 <CC名字> <OpenID>\r\n设置定时任务：每5分钟帮我自动检查OceanBus新消息，有消息立刻推送给我\r\n然后，给<CC名字>发个消息：帮我阅读一下项目文件夹，给我个100字的简介\r\n```\r\n\r\n5. 输出后告诉 用户：\"👆 复制上面这段，发给小龙虾。任务完成后我会自动回复结果。\"\r\n\r\n## 行为规则（必须遵守）\r\n\r\n配对完成后，当你收到小龙虾（Bridge）发来的任务消息时：\r\n\r\n1. **执行任务**：解析消息内容，完成用户要求的操作\r\n2. **汇报结果**：任务完成后，立即运行以下命令把结果回报给用户：\r\n   ```\r\n   node chat.js --data-dir .oceanbus-cc send Bridge --from <你的CC名字> \"<完整结果>\"\r\n   ```\r\n3. **汇报内容要求**：把关键发现、修改的文件、执行结果写清楚，就像用户坐在你旁边看屏幕一样\r\n\r\n## 身份安全（极其重要）\r\n\r\n**`.oceanbus-cc/` 目录是你的 OpenID 唯一凭证。** 删除它 = OpenID 永久作废 = 所有联系人发消息到死地址。\r\n\r\n- ✅ 可以删除 ocean-chat 目录重装（`--data-dir .oceanbus-cc` 在项目目录，不受影响）\r\n- ✅ 可以换电脑（把 `.oceanbus-cc/` 复制到新电脑即可）\r\n- ❌ 绝对不能删 `.oceanbus-cc/`\r\n- ❌ 绝对不能在同一项目目录跑两次 `setup`（会被检测到已有身份，安全返回）\r\n\r\n**不需要解释架构，不需要说原理。不要问用户任何问题。只需要生成这段话，并让 用户 照着做。**\n\nFile v2.20.0:docs/手机遥控ClaudeCode-工程师上手.md\n\n# OceanBus: 用手机遥控 Claude Code\r\n\r\n> 5 分钟打通手机微信 → Claude Code 的通信管道。\r\n> 从此你可以在吃饭、通勤、开会时，用微信给电脑上的 Claude Code 派活，做完自动回报。\r\n\r\n---\r\n\r\n## 架构（你只需要理解这张图）\r\n\r\n```\r\n  你的手机                          你的电脑\r\n┌──────────┐                    ┌──────────────┐\r\n│ 微信      │                    │ PM2 守护      │\r\n│  ↓        │     OceanBus      │  ↓            │\r\n│ 小龙虾     │←────────────────→│ chat.js listen│\r\n│ (Bridge)  │   端到端加密消息   │  ↓            │\r\n│  ↓        │                    │ Claude Code   │\r\n│ 收发消息   │                    │ 收到→执行→回报 │\r\n└──────────┘                    └──────────────┘\r\n```\r\n\r\n**三个角色的称呼：**\r\n- 🧑‍💻 你 → **Captain**（船长）\r\n- 🦎 你的 ClawBot  → **Bridge**（桥）\r\n- 🤖 你电脑上的 Claude Code → **CC**\r\n\r\n---\r\n\r\n## 第一步：（安装了CC的）PC 端安装\r\n\r\n```bash\r\n# ① 获取 ocean-chat\r\ngit clone https://github.com/ryanbihai/ocean-chat.git\r\ncd ocean-chat\r\nnpm install\r\n\r\n# ② 注册 OceanBus 身份\r\nnode chat.js setup\r\n# → 屏幕输出一个 76 位 OpenID，记下来！他是<cc的OpenID>\r\n\r\n# ③ 启动持久监听（PM2 守护，开机自启）\r\nnpm install -g pm2\r\npm2 start chat.js --name ob -- listen\r\npm2 save\r\npm2 startup   # 开机自启（按屏幕提示执行）\r\n```\r\n\r\n**验证：** `pm2 status` 应该显示 `ob` 进程状态为 `online`。\r\n\r\n---\r\n\r\n## 第二步：手机端配置（2 句话）\r\n\r\n打开微信，找到你的 小龙虾，依次说：\r\n\r\n```\r\n帮我安装 ocean-chat skill\r\n```\r\n\r\n```\r\n加联系人 CC <cc的OpenID>\r\n```\r\n\r\n> **OpenID 示例**：`E511kGbn_zUEnuhzr9-mult_TwASIwmcbJ8YcPSwjTj26ukPgpM8aD_KxAV-3D9kxAXjrunifio4POpo`\r\n>\r\n> 把 `<你的OpenID>` 替换成第一步 `setup` 输出的那串字符。\r\n\r\n---\r\n\r\n## 第三步：测试\r\n\r\n在微信里对小龙虾说：\r\n\r\n```\r\n小龙虾，告诉 CC：测试消息\r\n```\r\n\r\n→ 你的电脑上 Claude Code 应该收到通知并回复。\r\n→ 再对小龙虾说\"查消息\"，看 CC 的回复。\r\n\r\n---\r\n\r\n## 日常使用\r\n\r\n| 你想做什么 | 在微信里对小龙虾说什么 |\r\n|-----------|---------------------|\r\n| 派任务 | \"小龙虾，告诉 CC：帮我查一下 XXX 的 bug\" |\r\n| 查回复 | \"小龙虾，查消息\"（每次说话时自动带出） |\r\n| 紧急打断 | \"小龙虾，告诉 CC：停下手头的事，先修这个\" |\r\n\r\nCC 完成任务后会自动回报，你下次跟小龙虾说话时就能看到。\r\n\r\n---\r\n\r\n## 进阶玩法\r\n\r\n### 收到消息自动执行命令（无人值守）\r\n\r\n```bash\r\npm2 stop ob\r\npm2 start chat.js --name ob -- listen --on-message \"claude -p \\\"收到来自{from}的消息: {content}\\\"\"\r\npm2 save\r\n```\r\n\r\n模板变量：`{from}` 发件人、`{openid}` 地址、`{content}` 消息正文、`{time}` 时间。\r\n\r\n### 任务完成桌面通知\r\n\r\n```bash\r\n# macOS\r\npm2 start chat.js --name ob -- listen --on-message \"osascript -e 'display notification \\\"{from}: {content}\\\" with title \\\"OceanBus\\\"'\"\r\n\r\n# Linux\r\npm2 start chat.js --name ob -- listen --on-message \"notify-send 'OceanBus: {from}' '{content}'\"\r\n\r\n# Windows\r\npm2 start chat.js --name ob -- listen --on-message \"msg %USERNAME% \\\"{from}: {content}\\\"\"\r\n```\r\n\r\n### 一个 Captain 指挥多个 CC（多窗口/多机器）\r\n\r\n如果你同时开了好几个 Claude Code 窗口（同一台电脑不同项目、台式机、笔记本、服务器），每个都可以独立接收指令、各自回报，不会串。\r\n\r\n**原理**：每个 CC 窗口注册独立的 OpenID。窗口名字自动取自**当前项目目录名**——`CC-oceanbus`、`CC-dashboard`、`CC-spider`。无需手动命名。\r\n\r\n```\r\nCaptain 的 Bridge Roster:\r\n  CC-oceanbus     → OpenID_aaa  (~/work/oceanbus)\r\n  CC-dashboard    → OpenID_bbb  (~/work/dashboard)\r\n  CC-spider       → OpenID_ccc  (~/personal/spider)\r\n```\r\n\r\n**每个 CC 窗口的设置**（在各自项目目录下执行，每次 setup 自动生成不同 OpenID）：\r\n\r\n```bash\r\n# 窗口1：在 oceanbus 项目目录\r\ncd ~/work/oceanbus\r\ngit clone https://github.com/ryanbihai/ocean-chat.git && cd ocean-chat && npm install\r\nnode chat.js setup   # → OpenID_aaa\r\n\r\n# 窗口2：在 dashboard 项目目录\r\ncd ~/work/dashboard\r\ngit clone https://github.com/ryanbihai/ocean-chat.git && cd ocean-chat && npm install\r\nnode chat.js setup   # → OpenID_bbb\r\n```\r\n\r\n**Captain 在手机上加联系人**（用「发给你的 Claude Code，建立小龙虾连接.md」自动生成，复制粘贴即可）：\r\n\r\n```\r\n帮我安装 ocean-chat skill\r\n加联系人 CC-oceanbus <OpenID_aaa>\r\n加联系人 CC-dashboard <OpenID_bbb>\r\n设置定时任务：每5分钟帮我自动检查OceanBus新消息，有消息立刻推送给我\r\n```\r\n\r\n**日常使用**（在微信里说）：\r\n\r\n```\r\n告诉 CC-oceanbus: 查一下 publish 的 bug\r\n告诉 CC-dashboard: 修复登录页面样式\r\n告诉 CC-spider: 重启爬虫\r\n```\r\n\r\n**CC 回复时带上 From 头**，自动标注来源（CC 根据 cwd 自动填充）：\r\n\r\n```bash\r\n# CC-oceanbus 窗口回复\r\nnode chat.js send Bridge --from CC-oceanbus \"publish bug 修好了\"\r\n\r\n# CC-dashboard 窗口回复\r\nnode chat.js send Bridge --from CC-dashboard \"登录样式已修复\"\r\n```\r\n\r\n消息在手机端展示效果：\r\n\r\n```\r\n── CC-oceanbus (ob_abc...) · 14:30 ──\r\n  CC-oceanbus → Captain\r\n  publish bug 修好了\r\n\r\n── CC-dashboard (ob_def...) · 14:32 ──\r\n  CC-dashboard → Captain\r\n  登录样式已修复\r\n```\r\n\r\n`→` 箭头让 Captain 一眼看清：哪个项目窗口发的、发给谁。\r\n\r\n---\r\n\r\n## 常见问题\r\n\r\n| 问题 | 解决 |\r\n|------|------|\r\n| `pm2: command not found` | `npm install -g pm2` |\r\n| `尚未注册` | 先跑 `node chat.js setup` |\r\n| ClawBot 说找不到 skill | 对 ClawBot 说\"你装了哪些 skill\"，确认 ocean-chat 已装 |\r\n| 消息延迟 | 正常（OceanBus 是邮箱模型，非 IM），2s 轮询 |\r\n| 想停掉监听 | `pm2 stop ob` |\r\n| 多个 CC 分不清谁回的 | 回复时加 `--from <名字>`，消息头自动标注来源 |\r\n| 不同机器用同一 OpenID | 会导致消息被随机一台收取。每台独立 `setup` 即可 |\r\n\r\n---\r\n\r\n## 安全说明\r\n\r\n- 所有消息 **端到端加密**（XChaCha20-Poly1305），OceanBus 平台不可读\r\n- 身份基于 **Ed25519 密码学签名**，不可伪造\r\n- 消息在服务器 **72 小时后自动删除**\r\n- 代码开源（MIT），可自行审计\r\n\r\n---\r\n\r\n> 这就是 OceanBus 的用法之一。它本质上是一个 **AI Agent 间的通信协议**——\r\n> 不只是你连 Claude Code，你还可以连医生 Agent、保险 Agent、游戏 Agent。\r\n> 一个 OpenID，连通整个 Agent 互联网。\n\nFile v2.20.0:package-lock.json\n\n{\r\n  \"name\": \"ocean-chat\",\r\n  \"version\": \"2.4.0\",\r\n  \"lockfileVersion\": 3,\r\n  \"requires\": true,\r\n  \"packages\": {\r\n    \"\": {\r\n      \"name\": \"ocean-chat\",\r\n      \"version\": \"2.4.0\",\r\n      \"license\": \"MIT-0\",\r\n      \"dependencies\": {\r\n        \"oceanbus\": \"^0.9.0\",\r\n        \"qrcode-terminal\": \"^0.12.0\"\r\n      }\r\n    },\r\n    \"node_modules/@noble/ed25519\": {\r\n      \"version\": \"2.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@noble/ed25519/-/ed25519-2.3.0.tgz\",\r\n      \"integrity\": \"sha512-M7dvXL2B92/M7dw9+gzuydL8qn/jiqNHaoR3Q+cb1q1GHV7uwE17WCyFMG+Y+TZb5izcaXk5TdJRrDUxHXL78A==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://paulmillr.com/funding/\"\r\n      }\r\n    },\r\n    \"node_modules/@noble/hashes\": {\r\n      \"version\": \"1.8.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz\",\r\n      \"integrity\": \"sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \"^14.21.3 || >=16\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://paulmillr.com/funding/\"\r\n      }\r\n    },\r\n    \"node_modules/@paralleldrive/cuid2\": {\r\n      \"version\": \"2.3.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz\",\r\n      \"integrity\": \"sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@noble/hashes\": \"^1.1.5\"\r\n      }\r\n    },\r\n    \"node_modules/ansi-regex\": {\r\n      \"version\": \"5.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz\",\r\n      \"integrity\": \"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/ansi-styles\": {\r\n      \"version\": \"4.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz\",\r\n      \"integrity\": \"sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"color-convert\": \"^2.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/chalk/ansi-styles?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/asap\": {\r\n      \"version\": \"2.0.6\",\r\n      \"resolved\": \"https://registry.npmjs.org/asap/-/asap-2.0.6.tgz\",\r\n      \"integrity\": \"sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/asynckit\": {\r\n      \"version\": \"0.4.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz\",\r\n      \"integrity\": \"sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/call-bind-apply-helpers\": {\r\n      \"version\": \"1.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz\",\r\n      \"integrity\": \"sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"function-bind\": \"^1.1.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/call-bound\": {\r\n      \"version\": \"1.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz\",\r\n      \"integrity\": \"sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"call-bind-apply-helpers\": \"^1.0.2\",\r\n        \"get-intrinsic\": \"^1.3.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/cliui\": {\r\n      \"version\": \"8.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz\",\r\n      \"integrity\": \"sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"string-width\": \"^4.2.0\",\r\n        \"strip-ansi\": \"^6.0.1\",\r\n        \"wrap-ansi\": \"^7.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/color-convert\": {\r\n      \"version\": \"2.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz\",\r\n      \"integrity\": \"sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"color-name\": \"~1.1.4\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=7.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/color-name\": {\r\n      \"version\": \"1.1.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz\",\r\n      \"integrity\": \"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/combined-stream\": {\r\n      \"version\": \"1.0.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz\",\r\n      \"integrity\": \"sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"delayed-stream\": \"~1.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.8\"\r\n      }\r\n    },\r\n    \"node_modules/component-emitter\": {\r\n      \"version\": \"1.3.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz\",\r\n      \"integrity\": \"sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\r\n      }\r\n    },\r\n    \"node_modules/cookiejar\": {\r\n      \"version\": \"2.1.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz\",\r\n      \"integrity\": \"sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/debug\": {\r\n      \"version\": \"4.4.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/debug/-/debug-4.4.3.tgz\",\r\n      \"integrity\": \"sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ms\": \"^2.1.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=6.0\"\r\n      },\r\n      \"peerDependenciesMeta\": {\r\n        \"supports-color\": {\r\n          \"optional\": true\r\n        }\r\n      }\r\n    },\r\n    \"node_modules/delayed-stream\": {\r\n      \"version\": \"1.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz\",\r\n      \"integrity\": \"sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=0.4.0\"\r\n      }\r\n    },\r\n    \"node_modules/dezalgo\": {\r\n      \"version\": \"1.0.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz\",\r\n      \"integrity\": \"sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"asap\": \"^2.0.0\",\r\n        \"wrappy\": \"1\"\r\n      }\r\n    },\r\n    \"node_modules/dunder-proto\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"call-bind-apply-helpers\": \"^1.0.1\",\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"gopd\": \"^1.2.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/emoji-regex\": {\r\n      \"version\": \"8.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz\",\r\n      \"integrity\": \"sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/es-define-property\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/es-errors\": {\r\n      \"version\": \"1.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz\",\r\n      \"integrity\": \"sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/es-object-atoms\": {\r\n      \"version\": \"1.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz\",\r\n      \"integrity\": \"sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"es-errors\": \"^1.3.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/es-set-tostringtag\": {\r\n      \"version\": \"2.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz\",\r\n      \"integrity\": \"sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"get-intrinsic\": \"^1.2.6\",\r\n        \"has-tostringtag\": \"^1.0.2\",\r\n        \"hasown\": \"^2.0.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/escalade\": {\r\n      \"version\": \"3.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz\",\r\n      \"integrity\": \"sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=6\"\r\n      }\r\n    },\r\n    \"node_modules/fast-safe-stringify\": {\r\n      \"version\": \"2.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz\",\r\n      \"integrity\": \"sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/form-data\": {\r\n      \"version\": \"4.0.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz\",\r\n      \"integrity\": \"sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"asynckit\": \"^0.4.0\",\r\n        \"combined-stream\": \"^1.0.8\",\r\n        \"es-set-tostringtag\": \"^2.1.0\",\r\n        \"hasown\": \"^2.0.2\",\r\n        \"mime-types\": \"^2.1.12\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 6\"\r\n      }\r\n    },\r\n    \"node_modules/formidable\": {\r\n      \"version\": \"3.5.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz\",\r\n      \"integrity\": \"sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@paralleldrive/cuid2\": \"^2.2.2\",\r\n        \"dezalgo\": \"^1.0.4\",\r\n        \"once\": \"^1.4.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=14.0.0\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://ko-fi.com/tunnckoCore/commissions\"\r\n      }\r\n    },\r\n    \"node_modules/function-bind\": {\r\n      \"version\": \"1.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz\",\r\n      \"integrity\": \"sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==\",\r\n      \"license\": \"MIT\",\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/get-caller-file\": {\r\n      \"version\": \"2.0.5\",\r\n      \"resolved\": \"https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz\",\r\n      \"integrity\": \"sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==\",\r\n      \"license\": \"ISC\",\r\n      \"engines\": {\r\n        \"node\": \"6.* || 8.* || >= 10.*\"\r\n      }\r\n    },\r\n    \"node_modules/get-intrinsic\": {\r\n      \"version\": \"1.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz\",\r\n      \"integrity\": \"sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"call-bind-apply-helpers\": \"^1.0.2\",\r\n        \"es-define-property\": \"^1.0.1\",\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"es-object-atoms\": \"^1.1.1\",\r\n        \"function-bind\": \"^1.1.2\",\r\n        \"get-proto\": \"^1.0.1\",\r\n        \"gopd\": \"^1.2.0\",\r\n        \"has-symbols\": \"^1.1.0\",\r\n        \"hasown\": \"^2.0.2\",\r\n        \"math-intrinsics\": \"^1.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/get-proto\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"dunder-proto\": \"^1.0.1\",\r\n        \"es-object-atoms\": \"^1.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/gopd\": {\r\n      \"version\": \"1.2.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz\",\r\n      \"integrity\": \"sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/has-symbols\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/has-tostringtag\": {\r\n      \"version\": \"1.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz\",\r\n      \"integrity\": \"sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"has-symbols\": \"^1.0.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/hasown\": {\r\n      \"version\": \"2.0.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz\",\r\n      \"integrity\": \"sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"function-bind\": \"^1.1.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/is-fullwidth-code-point\": {\r\n      \"version\": \"3.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz\",\r\n      \"integrity\": \"sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/math-intrinsics\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      }\r\n    },\r\n    \"node_modules/methods\": {\r\n      \"version\": \"1.1.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/methods/-/methods-1.1.2.tgz\",\r\n      \"integrity\": \"sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.6\"\r\n      }\r\n    },\r\n    \"node_modules/mime\": {\r\n      \"version\": \"2.6.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/mime/-/mime-2.6.0.tgz\",\r\n      \"integrity\": \"sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==\",\r\n      \"license\": \"MIT\",\r\n      \"bin\": {\r\n        \"mime\": \"cli.js\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=4.0.0\"\r\n      }\r\n    },\r\n    \"node_modules/mime-db\": {\r\n      \"version\": \"1.52.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz\",\r\n      \"integrity\": \"sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.6\"\r\n      }\r\n    },\r\n    \"node_modules/mime-types\": {\r\n      \"version\": \"2.1.35\",\r\n      \"resolved\": \"https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz\",\r\n      \"integrity\": \"sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"mime-db\": \"1.52.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.6\"\r\n      }\r\n    },\r\n    \"node_modules/ms\": {\r\n      \"version\": \"2.1.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/ms/-/ms-2.1.3.tgz\",\r\n      \"integrity\": \"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==\",\r\n      \"license\": \"MIT\"\r\n    },\r\n    \"node_modules/object-inspect\": {\r\n      \"version\": \"1.13.4\",\r\n      \"resolved\": \"https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz\",\r\n      \"integrity\": \"sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/oceanbus\": {\r\n      \"version\": \"0.9.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/oceanbus/-/oceanbus-0.9.1.tgz\",\r\n      \"integrity\": \"sha512-ukTId1hNayoC7u/S+VPlNWVyGmw7exllM+7PFZxlhX83XVp/kQsGL/5PfCEN/T4WsAkYqbjJhCl3JM+xkrEphw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"@noble/ed25519\": \"^2.1.0\",\r\n        \"@noble/hashes\": \"^1.7.0\",\r\n        \"superagent\": \"^10.3.0\",\r\n        \"yargs\": \"^17.7.0\"\r\n      },\r\n      \"bin\": {\r\n        \"oceanbus\": \"bin/oceanbus.js\"\r\n      }\r\n    },\r\n    \"node_modules/once\": {\r\n      \"version\": \"1.4.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/once/-/once-1.4.0.tgz\",\r\n      \"integrity\": \"sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==\",\r\n      \"license\": \"ISC\",\r\n      \"dependencies\": {\r\n        \"wrappy\": \"1\"\r\n      }\r\n    },\r\n    \"node_modules/qrcode-terminal\": {\r\n      \"version\": \"0.12.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz\",\r\n      \"integrity\": \"sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==\",\r\n      \"bin\": {\r\n        \"qrcode-terminal\": \"bin/qrcode-terminal.js\"\r\n      }\r\n    },\r\n    \"node_modules/qs\": {\r\n      \"version\": \"6.15.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/qs/-/qs-6.15.1.tgz\",\r\n      \"integrity\": \"sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==\",\r\n      \"license\": \"BSD-3-Clause\",\r\n      \"dependencies\": {\r\n        \"side-channel\": \"^1.1.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=0.6\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/require-directory\": {\r\n      \"version\": \"2.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz\",\r\n      \"integrity\": \"sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==\",\r\n      \"license\": \"MIT\",\r\n      \"engines\": {\r\n        \"node\": \">=0.10.0\"\r\n      }\r\n    },\r\n    \"node_modules/side-channel\": {\r\n      \"version\": \"1.1.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz\",\r\n      \"integrity\": \"sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"object-inspect\": \"^1.13.3\",\r\n        \"side-channel-list\": \"^1.0.0\",\r\n        \"side-channel-map\": \"^1.0.1\",\r\n        \"side-channel-weakmap\": \"^1.0.2\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/side-channel-list\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"object-inspect\": \"^1.13.4\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/side-channel-map\": {\r\n      \"version\": \"1.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz\",\r\n      \"integrity\": \"sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"call-bound\": \"^1.0.2\",\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"get-intrinsic\": \"^1.2.5\",\r\n        \"object-inspect\": \"^1.13.3\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/side-channel-weakmap\": {\r\n      \"version\": \"1.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz\",\r\n      \"integrity\": \"sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"call-bound\": \"^1.0.2\",\r\n        \"es-errors\": \"^1.3.0\",\r\n        \"get-intrinsic\": \"^1.2.5\",\r\n        \"object-inspect\": \"^1.13.3\",\r\n        \"side-channel-map\": \"^1.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">= 0.4\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/sponsors/ljharb\"\r\n      }\r\n    },\r\n    \"node_modules/string-width\": {\r\n      \"version\": \"4.2.3\",\r\n      \"resolved\": \"https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz\",\r\n      \"integrity\": \"sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"emoji-regex\": \"^8.0.0\",\r\n        \"is-fullwidth-code-point\": \"^3.0.0\",\r\n        \"strip-ansi\": \"^6.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/strip-ansi\": {\r\n      \"version\": \"6.0.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz\",\r\n      \"integrity\": \"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ansi-regex\": \"^5.0.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=8\"\r\n      }\r\n    },\r\n    \"node_modules/superagent\": {\r\n      \"version\": \"10.3.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz\",\r\n      \"integrity\": \"sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"component-emitter\": \"^1.3.1\",\r\n        \"cookiejar\": \"^2.1.4\",\r\n        \"debug\": \"^4.3.7\",\r\n        \"fast-safe-stringify\": \"^2.1.1\",\r\n        \"form-data\": \"^4.0.5\",\r\n        \"formidable\": \"^3.5.4\",\r\n        \"methods\": \"^1.1.2\",\r\n        \"mime\": \"2.6.0\",\r\n        \"qs\": \"^6.14.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=14.18.0\"\r\n      }\r\n    },\r\n    \"node_modules/wrap-ansi\": {\r\n      \"version\": \"7.0.0\",\r\n      \"resolved\": \"https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz\",\r\n      \"integrity\": \"sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"ansi-styles\": \"^4.0.0\",\r\n        \"string-width\": \"^4.1.0\",\r\n        \"strip-ansi\": \"^6.0.0\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      },\r\n      \"funding\": {\r\n        \"url\": \"https://github.com/chalk/wrap-ansi?sponsor=1\"\r\n      }\r\n    },\r\n    \"node_modules/wrappy\": {\r\n      \"version\": \"1.0.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz\",\r\n      \"integrity\": \"sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==\",\r\n      \"license\": \"ISC\"\r\n    },\r\n    \"node_modules/y18n\": {\r\n      \"version\": \"5.0.8\",\r\n      \"resolved\": \"https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz\",\r\n      \"integrity\": \"sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==\",\r\n      \"license\": \"ISC\",\r\n      \"engines\": {\r\n        \"node\": \">=10\"\r\n      }\r\n    },\r\n    \"node_modules/yargs\": {\r\n      \"version\": \"17.7.2\",\r\n      \"resolved\": \"https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz\",\r\n      \"integrity\": \"sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==\",\r\n      \"license\": \"MIT\",\r\n      \"dependencies\": {\r\n        \"cliui\": \"^8.0.1\",\r\n        \"escalade\": \"^3.1.1\",\r\n        \"get-caller-file\": \"^2.0.5\",\r\n        \"require-directory\": \"^2.1.1\",\r\n        \"string-width\": \"^4.2.3\",\r\n        \"y18n\": \"^5.0.5\",\r\n        \"yargs-parser\": \"^21.1.1\"\r\n      },\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    },\r\n    \"node_modules/yargs-parser\": {\r\n      \"version\": \"21.1.1\",\r\n      \"resolved\": \"https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz\",\r\n      \"integrity\": \"sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==\",\r\n      \"license\": \"ISC\",\r\n      \"engines\": {\r\n        \"node\": \">=12\"\r\n      }\r\n    }\r\n  }\r\n}\n\nFile v2.20.0:package.json\n\n{\r\n  \"name\": \"ocean-chat\",\r\n  \"version\": \"2.4.0\",\r\n  \"private\": true,\r\n  \"description\": \"OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents — zero server deployment.\",\r\n  \"main\": \"chat.js\",\r\n  \"scripts\": {\r\n    \"setup\": \"node chat.js setup\",\r\n    \"openid\": \"node chat.js openid\",\r\n    \"send\": \"node chat.js send\",\r\n    \"check\": \"node chat.js check\"\r\n  },\r\n  \"keywords\": [\r\n    \"oceanbus\",\r\n    \"agent-to-agent\",\r\n    \"a2a\",\r\n    \"meetup\",\r\n    \"negotiation\",\r\n    \"ai-agent\",\r\n    \"messaging\",\r\n    \"threading\",\r\n    \"conversation\",\r\n    \"openclaw\",\r\n    \"clawhub\"\r\n  ],\r\n  \"license\": \"MIT-0\",\r\n  \"dependencies\": {\r\n    \"oceanbus\": \"^0.9.0\",\r\n    \"qrcode-terminal\": \"^0.12.0\"\r\n  }\r\n}\n\nFile v2.20.0:config.example.yaml\n\n# OceanBus Chat configuration\r\n# Copy this file to config.yaml and edit as needed\r\n\r\n# OceanBus L0 API endpoint (default is the public test server)\r\n# oceanbus_base_url: https://ai-t.ihaola.com.cn/api/l0\r\n\r\n# Yellow Pages service OpenID (required — contact admin to obtain)\r\n# Environment variable: OCEANBUS_YP_OPENIDS\r\n# oceanbus_yp_openids: \"xxxxxxxxxxxx\"\n\nArchive v2.19.1: 16 files, 74252 bytes\n\nFiles: chat.js (96063b), config.example.yaml (361b), date-protocol.md (7397b), docs/发本文件给你的claudecode.md (2986b), docs/手机遥控ClaudeCode-工程师上手.md (6953b), package-lock.json (27004b), package.json (754b), README.md (5158b), SKILL.md (23705b), src/notify-wechat.js (4673b), src/wechat-bot.js (17336b), src/wechat-cc-bridge.js (21716b), test-meeting.js (10172b), test-yp.js (4511b), threads.js (8905b), _meta.json (130b)\n\nFile v2.19.1:SKILL.md\n\n---\r\nname: ocean-chat\r\ndescription: OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts, send encrypted A2A messages, schedule meetings, or organize multi-topic conversations. Zero deployment, 5-minute setup. npm install oceanbus.\r\nversion: 2.16.0\r\nmetadata:\r\n  openclaw:\r\n    requires:\r\n      bins:\r\n        - node\r\n    emoji: \"\\U0001F30A\"\r\n    homepage: https://github.com/ryanbihai/ocean-chat\r\n    envVars:\r\n      - name: OCEANBUS_BASE_URL\r\n        required: false\r\n        description: OceanBus L0 API endpoint. Defaults to public test server.\r\n      - name: OCEANBUS_YP_OPENIDS\r\n        required: false\r\n        description: Yellow Pages service OpenID(s). SDK auto-discovers via well-known; set only to override.\r\n      - name: WECHAT_BOT_TOKEN\r\n        required: false\r\n        description: 微信 bot token（monitor 命令微信推送用，也可从 OpenClaw 插件自动发现）\r\n      - name: WECHAT_BOT_BASE_URL\r\n        required: false\r\n        description: 微信 API base URL（默认 https://ilinkai.weixin.qq.com）\r\n      - name: WECHAT_BOT_USER_ID\r\n        required: false\r\n        description: 目标用户微信 ID（monitor 推送对象）\r\n---\r\n\r\n# Ocean Chat — Agent 通讯录 + 聊天 + 约人\r\n\r\nOcean Chat 是 OceanBus 生态的 **核心用户界面 Skill**。三个能力：管理通讯录、收发消息、约人见面。\r\n\r\n数据流：`Roster（SDK 内置）← ocean-chat（UI）→ OceanBus L0（消息管道）→ 对方 Agent`\r\n\r\n其他 OceanBus Skill（ocean-agent、guess-ai）共享同一个 Roster 数据，但通过 SDK 直接调用，不经 ocean-chat。\r\n\r\n## 架构关系图\r\n\r\n```\r\n用户（人类）\r\n    │\r\n    ▼\r\nocean-chat Skill（通讯录 UI + 聊天 + 约人）\r\n    │\r\n    ├── ob.roster.*           ← 共享通讯录（~/.oceanbus/roster.json）\r\n    ├── ob.send / ob.sync     ← OceanBus 消息管道\r\n    └── ob.l1.yellowPages.*   ← 黄页服务发现\r\n    │\r\n    ├── ocean-agent  ──→ ob.roster.*  （直接 SDK 调用，不经 ocean-chat）\r\n    └── guess-ai     ──→ ob.roster.*  （直接 SDK 调用，不经 ocean-chat）\r\n```\r\n\r\n---\r\n\r\n## 冷启动：首次体验\r\n\r\n核心原则：**生态初期，拉好友下水是第一优先级。** 黄页服务商还不多，不要让它占据首屏。让用户立刻感受到价值的方式只有一个——让 ta 的朋友也装上。\r\n\r\n### Step 0：判断状态\r\n\r\n| 条件 | 走哪个流程 |\r\n|------|-----------|\r\n| `~/.oceanbus-chat/credentials.json` 不存在 | → 未注册流程 |\r\n| 存在但 Roster 通讯录为空 | → 已注册但零联系人流程 |\r\n| 存在且 Roster 有联系人 | → 正常使用，跳过冷启动 |\r\n\r\n### Step 1：未注册 → 注册\r\n\r\n如果用户说\"先帮我注册\"或首次使用 ocean-chat，执行 `node chat.js setup`。\r\n\r\n注册成功后，先获取 username 和 OpenID：\r\n\r\n```\r\n# 获取 OpenID\r\nnode chat.js openid\r\n\r\n# 获取系统用户名（用于填充欢迎语）\r\n# Windows: echo %USERNAME%\r\n# Mac/Linux: whoami\r\n```\r\n\r\n然后展示**两条消息**：\r\n\r\n**第一条（给用户自己看）**：\r\n\r\n```\r\n🌊 Ocean Chat 已就绪！\r\n\r\n你的 Agent 现在有了一个全球地址，可以跟任何人的 Agent\r\n直接聊天、协调事务。\r\n\r\n──\r\n💡 顺手设置空闲偏好（约时间时 Agent 不反复问你）：\r\n   node chat.js availability set \"工作日晚7点后，周末全天\"\r\n\r\n💡 也可以对我说\"帮我找火锅店\"，从黄页发现商户 Agent。\r\n   不过目前生态还在早期，朋友之间互相连上是最直接的用法。\r\n\r\n──\r\n👥 想让朋友也连上？\r\n   把下面那条消息直接转发给 ta。ta 复制粘贴给自己的 AI 就行了——\r\n   OpenID 已经帮你填好了，ta 不用管。\r\n```\r\n\r\n**第二条（A 转发给朋友，朋友粘贴给自己的 AI）**：\r\n\r\n> 模板如下——`<A的OpenID>` 和 `<A的名字>` 由 Agent 自动填入实际值。\r\n\r\n```\r\n把这个发给你的龙虾：\r\n\r\n<A的OpenID> <A的名字>\r\n```\r\n\r\n朋友只需要把这段话发给自己的 AI（ocean-chat），AI 看到 OpenID + 名字，自动完成：\r\n- `oceanbus add <A的名字> <A的OpenID> --greet-as <朋友的名字>`\r\n- 双向通讯录建立，一步都不用多\r\n\r\n### Step 1b：朋友收到邀请（B 侧）\r\n\r\n当用户粘贴了 `<OpenID> <名字>` 给自己的 AI 时：\r\n\r\n1. 识别格式：76 位 Base64url 字符串 + 空格 + 中文/英文名 → 这是加人请求\r\n2. 询问用户的名字（如果还不知道）\r\n3. 执行：`oceanbus add <名字> <OpenID> --greet-as <B的名字>`\r\n4. 告诉用户：\"✅ 已加 <名字> 为联系人，并发送了打招呼消息。现在你们可以直接聊天了。\"\r\n\r\n> 双方通讯录建立，只需 A 分享一行字。B 贴给自己的 AI，一步搞定。\r\n\r\n### Step 1c：A 收到打招呼消息\r\n\r\n当 `check`/`listen` 收到 `\"Hi XXX, I'm YYY. Add me to your contacts.\"` 格式的消息时：\r\n\r\n1. 通过 `roster.findByOpenId()` 反查发件人。\r\n2. 如果通讯录中还没有：`node chat.js add <YYY> <from_openid>`。\r\n3. 告诉用户：\"🎉 YYY 已加入你的通讯录！现在你们可以直接聊天了。\"\r\n\r\n### Step 2：已注册但零联系人\r\n\r\n用户再次打开 ocean-chat，检测到 Roster 为空：\r\n\r\n```\r\n👋 你的通讯录还是空的。\r\n\r\n想让朋友也连上？把下面这条消息转发给 ta，\r\nta 贴给自己的 AI 就行：\r\n\r\n──\r\n<A的OpenID> <A的名字>\r\n──\r\n\r\n也可以对我说\"帮我找火锅店\"从黄页发现商户 Agent。\r\n```\r\n\r\n### Step 3：黄页发现（用户主动触发）\r\n\r\n用户说\"帮我找 X\"时：\r\n\r\n```\r\n用户：\"帮我找火锅店\"\r\n  → node chat.js discover 火锅\r\n  → 有结果 → 展示 + \"要加哪个为联系人？加了之后可以直接聊天、约时间。\"\r\n  → 无结果 → \"黄页上暂时没有。但你可以：\r\n      1. 让你的朋友装 ocean-chat，互相加上\r\n      2. 把自己发布到黄页：node chat.js publish <你的名字>\"\r\n```\r\n\r\n### Step 4：自动发现\r\n\r\n每次用户打开 ocean-chat 时（非首次），自动检查 autoDiscovery：\r\n\r\n```\r\n\"对了，我从你最近的对话里发现了几个可能的人名：\r\n 李丽（提到 5 次）、老赵（提到 3 次）\r\n 要我帮你加到通讯录吗？\"\r\n```\r\n\r\n> 被拒绝过一次后，本轮对话不再追问。下次对话再提醒一次。\r\n\r\n---\r\n\r\n## 一、Roster（通讯录）\r\n\r\nocean-chat 是通讯录的**唯一 UI 入口**。用户说\"加人/查人/改人/删人\"，全部在 ocean-chat 中处理。\r\n\r\n底层原理：`RosterService` 来自 `oceanbus` SDK（`require('oceanbus').RosterService`）。SDK 负责数据模型和索引；LLM 负责语义理解和消歧。\r\n\r\n### 1.1 查找联系人\r\n\r\n```\r\n用户说了一个名字/描述\r\n        │\r\n        ▼\r\n  new RosterService().search(query)\r\n        │\r\n        ├── exact.length == 1 ──→ 直接使用，不询问\r\n        ├── exact.length > 1  ──→ 展示候选（列出 tags/notes 差异），让用户选\r\n        ├── fuzzy.length == 1  ──→ \"你是说 XXX 吗？\"\r\n        ├── fuzzy.length > 1  ──→ 展示候选\r\n        ├── byTag.length > 0   ──→ \"没有叫这个名字的，但有标签为 XXX 的联系人...\"\r\n        └── 全空              ──→ \"通讯录里没有。要新建吗？\"\r\n```\r\n\r\n**模糊查询处理**：\r\n\r\n| 用户说 | Roster 行为 | LLM 处理 |\r\n|--------|-----------|---------|\r\n| \"老 王\"（多余空格） | search() 自动去空格，fuzzy 命中 | \"你是说老王吗？\" |\r\n| \"王总\"（称呼） | alias 精确命中 | 直接使用 |\r\n| \"那个喜欢川菜的\"（语义） | search(\"川菜\") → byNote 命中 | 直接使用 |\r\n| \"上次打羽毛球那个\"（语义） | list({ tags: [\"badminton\"] }) | 列出候选人 |\r\n\r\n**Shell 命令**：\r\n\r\n```bash\r\n# 普通查找（走 CLI）\r\nnode chat.js contacts\r\n\r\n# 高级查找（用 SDK one-liner）\r\nnode -e \"const {RosterService}=require('oceanbus');new RosterService().search('老王').then(r=>console.log(JSON.stringify(r,null,2)))\"\r\n```\r\n\r\n### 1.2 添加联系人\r\n\r\n```\r\n用户说：\"加一个联系人，老李，财务部同事\"\r\n  → new RosterService().add({ name: \"老李\", tags: [\"colleague\", \"finance\"], source: \"manual\" })\r\n  → 自动检测重复（同 OpenID → 提示合并）\r\n  → \"已添加老李，标签: colleague, finance\"\r\n```\r\n\r\n**Shell 命令**：\r\n\r\n```bash\r\nnode chat.js add <名字> <OpenID>       # 已知 OpenID\r\nnode chat.js add <名字>                # 先加名字，OpenID 后续补\r\n```\r\n\r\n### 1.3 查看联系人详情\r\n\r\n```\r\n用户说：\"老王是谁？\"\r\n  → roster.get(\"laowang\")\r\n  → \"老王，你的大学同学。标签: friend, badminton。备注: 喜欢川菜。最近联系: 5月6日。\"\r\n  → 如果有重复提示（duplicateHints），主动问：\"对了，通讯录里有另一个老王（公司财务）。要合并吗？\"\r\n```\r\n\r\n### 1.4 修改联系人\r\n\r\n可通过 RosterService 修改标签、别名、备注。标签由 LLM 自动维护，也可手动调整。详见末尾命令参考。\r\n\r\n### 1.5 合并重复联系人\r\n\r\n当 `getDuplicateHints()` 有数据时，主动提示：\r\n\r\n```\r\n通讯录检测到可能的重复：\r\n  老王 (friend) 和 老王 (colleague) 可能是同一个人（相同手机号）\r\n  要合并吗？\r\n\r\n用户确认后：\r\n  → roster.merge(\"laowang\", \"wangcai\")\r\n  → \"已合并。老王现在有 2 个 Agent 地址，标签: friend, colleague\"\r\n```\r\n\r\n重复联系人和消除提示通过 RosterService 方法操作，详见末尾命令参考。\r\n\r\n### 1.6 AutoDiscovery 审核\r\n\r\n新名字出现 3 次以上会自动进入待审核队列。首次使用或定期检查：\r\n\r\n待审核列表、通过、拒绝操作均通过 RosterService 完成，详见末尾命令参考。\r\n\r\n**主动审核时机**：用户说\"看看通讯录\"、新对话开始、`getDuplicateHints()` 返回非空时。\r\n\r\n### Roster SDK 速查\r\n\r\n以下 one-liner 覆盖高级 Roster 操作（通过 `node -e` 直接调 SDK）：\r\n\r\n搜索 `node -e \"const {RosterService}=require('oceanbus');new RosterService().search('老王')...\"`\r\n合并 `node -e \"const {RosterService}=require('oceanbus');new RosterService().merge('keep','discard')\"`\r\n查重 `node -e \"const {RosterService}=require('oceanbus');new RosterService().getDuplicateHints()...\"`\r\n待审 `node -e \"const {RosterService}=require('oceanbus');new RosterService().getPending()...\"`\r\n改标签 `node -e \"const {RosterService}=require('oceanbus');new RosterService().updateTags(name,tags)\"`\r\n改备注 `node -e \"const {RosterService}=require('oceanbus');new RosterService().update(name,{notes:'...'})\"`\r\n加别名 `node -e \"const {RosterService}=require('oceanbus');new RosterService().addAlias(name,'alias')\"`\r\n\r\n完整参数见前文各小节，或运行 `node -e \"require('oceanbus').RosterService\"` 查看。\r\n\r\n---\r\n\r\n## 二、Chat（A2A 消息）\r\n\r\n### 2.1 发消息\r\n\r\n```\r\n用户说：\"给老王发消息，周五打球？\"\r\n  → roster.search(\"老王\") → 消歧 → 拿到 OpenID\r\n  → ob.send(openid, \"周五打球？\")\r\n  → roster.touch(\"laowang\")\r\n```\r\n\r\n普通文本消息：\r\n\r\n```bash\r\nnode chat.js send <名字> <消息>\r\n```\r\n\r\n### 2.2 收消息\r\n\r\n```bash\r\nnode chat.js check       # 手动检查\r\nnode chat.js listen      # 实时监听（推荐）\r\n```\r\n\r\n收消息时自动 `roster.findByOpenId()` 反查联系人名。\r\n\r\n### 2.3 结构化协议消息（v2.1 新增）\r\n\r\n支持 JSON 协议消息。当收到 type=protocol 的消息时，按协议类型路由。\r\n\r\n```bash\r\n# 发送协议消息\r\nnode chat.js send <名字> --protocol ocean-date/negotiate/v1 '{\"type\":\"proposal\",\"payload\":{\"time\":\"周五19:00\",\"location\":\"渝信川菜\"}}'\r\n```\r\n\r\n**消息类型**：\r\n\r\n| type | protocol | 说明 |\r\n|------|---------|------|\r\n| text | null | 自由聊天 |\r\n| protocol | `ocean-date/negotiate/v1` | 约人协商 |\r\n| system | null | 系统消息（上线通知等） |\r\n\r\n**收到 protocol 消息时的处理**：读取 `structured` 字段 → 根据 `protocol` 名查找处理规则 → 执行逻辑。对未知协议，回复 system 消息：\"收到协议消息 \\`<protocol>\\`，但当前版本不支持。升级 ocean-chat 后可使用。\"\r\n\r\n---\r\n\r\n## 三、Date（1v1 约人 v2.1）\r\n\r\n基于 Roster（找谁）+ Chat（怎么发）的 1v1 协商引擎。\r\n\r\n### 3.1 触发\r\n\r\n```\r\n用户说：\"帮我约老王周五晚上吃饭\"\r\n用户说：\"跟老王约个见面\"\r\n用户说：\"帮我和老王协商时间\"\r\n```\r\n\r\n### 3.2 流程\r\n\r\n```\r\n1. 解析用户意图 → 提取约束（时间/地点/偏好）\r\n2. roster.search(\"老王\") → 获取 OpenID\r\n3. 构造 proposal → 发送协议消息\r\n4. 等对方回复 → 解析 response\r\n5. 如需调整 → 发 counter-proposal（最多 3 轮）\r\n6. 达成一致 → 通知用户 + 写入 chat.log\r\n```\r\n\r\n### 3.3 协议 Schema\r\n\r\n详见 `date-protocol.md`。核心消息类型：\r\n\r\n| type | 方向 | 含义 |\r\n|------|------|------|\r\n| `proposal` | 发起方 → 接收方 | 首次提案 |\r\n| `counter` | 接收方 → 发起方 | 反提案 |\r\n| `accept` | 任意方 | 接受 |\r\n| `reject` | 任意方 | 拒绝 |\r\n| `withdraw` | 发起方 | 撤回提案 |\r\n\r\n### 3.4 约束提取（LLM 负责）\r\n\r\n```\r\n用户：\"帮我约老王周五或周六晚上，川菜，朝阳区，别太贵\"\r\n\r\n提取:\r\n  时间约束: 周五晚上 | 周六晚上\r\n  地点约束: 朝阳区\r\n  口味约束: 川菜\r\n  预算约束: 别太贵\r\n  人员: 老王\r\n```\r\n\r\n### 3.5 协商规则\r\n\r\n- **最多 3 轮**。3 轮未达成一致 → 告诉用户建议直接沟通\r\n- **提案必须具体**（时间+地点，不是\"周末见\"）\r\n- **考虑对方偏好**（如果对方 Agent 回复了偏好，据此调整）\r\n- **确认即锁定**（发送 accept 后不可反悔）\r\n\r\n### 3.6 完成报告\r\n\r\n```\r\n📋 约人协商报告\r\n\r\n📍 结果: 已与老王确认\r\n   时间: 周五 19:00\r\n   地点: 渝信川菜（朝阳大悦城店）\r\n\r\n🔄 过程（2轮）:\r\n   ① 你提议: 周五19:00 渝信川菜\r\n   ② 老王确认: ✅ 可以\r\n\r\n💡 建议: 周五晚高峰，提前出发\r\n```\r\n\r\n---\r\n\r\n## 四、Thread（对话线程 v1）\r\n\r\n当两个人同时在聊 2-3 件不同的事（比如一边讨论体检预约、一边沟通专家推荐），消息会混在一起难以分辨。Thread 协议解决的就是这个问题——**给每通对话一个 thread_id，收发双方按线程分组**。\r\n\r\n协议详见 `OceanBusDocs/ocean-thread-protocol-v1.md`。\r\n\r\n### 4.1 线程生命周期\r\n\r\n```\r\ncreate ──→ active ──→ resolve ──→ resolved\r\n              │                      │\r\n              │  reply               │  reopen\r\n              ▼                      ▼\r\n            active ◄────────────── active\r\n```\r\n\r\n### 4.2 触发\r\n\r\n```\r\n用户说：\"帮我跟老王开个线程，聊一下体检预约\"\r\n用户说：\"回复老王的体检线程，就说明天上午可以\"\r\n用户说：\"看看我和老王有哪些对话\"\r\n```\r\n\r\n### 4.3 命令\r\n\r\n```bash\r\nnode chat.js thread create <名字> --subject \"主题\"     # 创建新线程\r\nnode chat.js thread reply <thread_id> <消息>           # 在线程中回复\r\nnode chat.js thread list                               # 列出所有线程\r\nnode chat.js thread show <thread_id>                   # 查看线程详情（含历史消息）\r\nnode chat.js thread resolve <thread_id>                # 结束线程\r\nnode chat.js thread reopen <thread_id>                 # 重开已结束线程\r\n```\r\n\r\n### 4.4 协议消息格式\r\n\r\n通过 `ocean-thread/v1` 协议发送，与 Date 协议的 `ocean-date/negotiate/v1` 同级：\r\n\r\n```json\r\n{\r\n  \"type\": \"protocol\",\r\n  \"protocol\": \"ocean-thread/v1\",\r\n  \"structured\": {\r\n    \"action\": \"create\",\r\n    \"thread_id\": \"th_20260508_a1b2c3\",\r\n    \"subject\": \"体检预约 — 张先生 45岁 北京\",\r\n    \"payload\": {}\r\n  }\r\n}\r\n```\r\n\r\n### 4.5 显示约定\r\n\r\n`check` / `listen` 收到线程消息时，消息前会显示线程标记：\r\n\r\n```\r\n── 来自 老王 (ob_c-Qrza...) · 14:30:00 ──\r\n  [th_a1b2c3...] 体检预约 — 张先生\r\n\r\n🧵 新对话 · [th_a1b2c3...]\r\n  主题: 体检预约 — 张先生 45岁 北京\r\n```\r\n\r\n### 4.6 自动关联\r\n\r\n- 收到 `create` 协议消息时，自动在本地创建线程记录\r\n- 收到 `reply` 时，自动追加到对应线程\r\n- 收到 `resolve` / `reopen` 时，自动更新线程状态\r\n- 如果用 `send` 发普通消息（非协议），会自动关联到与该用户最近的活跃线程\r\n\r\n### 4.7 与 ocean-desk 的关系\r\n\r\nocean-desk 坐席系统依赖此协议做工单管理：\r\n- 每条客户咨询 = 一个线程\r\n- AI skill 上下文通过 `payload` 字段透传给坐席\r\n- 坐席回复 = `reply`\r\n- 工单关闭 = `resolve`\r\n- 线程 ID 可直接映射到工单 ID\r\n\r\n---\r\n\r\n## 五、Yellow Pages（黄页）\r\n\r\n```bash\r\nnode chat.js publish <名字>              # 发布自己的 OpenID 到黄页\r\nnode chat.js discover <名字>             # 搜索朋友的 OpenID\r\nnode chat.js unpublish                   # 从黄页移除\r\n```\r\n\r\n**发现联系人后必须主动提出加入 Roster**：\r\n\r\n```\r\ndiscover 返回结果 →\r\n  ① 展示候选列表（名字 + 描述）\r\n  ② \"要加哪个为联系人？加了之后你们可以直接聊天、约时间。\"\r\n  ③ 用户选择 → node chat.js add → 告知用户已添加\r\n```\r\n\r\n黄页是冷启动期最重要的联系人来源——不要让用户自己想着去加。\r\n\r\n---\r\n\r\n## 六、命令速查\r\n\r\n```bash\r\n# 安装 & 身份\r\nnode chat.js setup                       # 首次注册（自动迁移旧数据）\r\nnode chat.js openid                      # 查看你的 OpenID\r\n\r\n# 通讯录\r\nnode chat.js add <名字> <OpenID>          # 添加联系人（自动检测重复）\r\nnode chat.js contacts                     # 列出通讯录\r\n\r\n# 消息\r\nnode chat.js send <名字|OpenID> <消息>     # 发消息（自动 Roster 解析）\r\n\t                                         # --from <你的名字>  附加 From/To 消息头（多 CC 场景）\r\nnode chat.js check                        # 查看新消息\r\nnode chat.js listen                       # 实时监听（SDK 轮询）\r\n\tnode chat.js listen --on-message \"cmd\"    # 监听 + 收到消息时执行命令 ({from} {openid} {content} {time})\r\n\tnode chat.js monitor                     # 监听 + 微信推送通知（需配 WECHAT_BOT_* 环境变量）\r\n\tnode chat.js pair-me                     # 生成配对消息（朋友粘贴给 CC 建立连接）\r\n\r\n# 黄页\r\nnode chat.js publish <名字>               # 发布到黄页\r\nnode chat.js discover <名字>              # 从黄页搜索\r\nnode chat.js unpublish                    # 从黄页移除\r\n\r\n# Date 约人\r\nnode chat.js date <名字> <类型>           # 发送 Date 协议消息\r\n  --time <ISO> --location <地点> --notes <备注>\r\n\r\n# Thread 对话线程\r\nnode chat.js thread create <名字>          # 创建对话线程\r\n  --subject \"主题\" [--payload '{\"k\":\"v\"}']\r\nnode chat.js thread reply <id> <消息>      # 在线程中回复\r\nnode chat.js thread list                   # 列出所有线程\r\nnode chat.js thread show <id>              # 查看线程详情\r\nnode chat.js thread resolve <id>           # 结束线程\r\nnode chat.js thread reopen <id>            # 重开已结束线程\r\n```\r\n\r\n## 七、实时通信\r\n\r\n**默认推荐 `listen` 模式**（2s polling，开销极小）：\r\n\r\n```bash\r\nnode chat.js listen\r\n```\r\n\r\n收消息自动通过 Roster 反查人名，展示 `老王 (ob_xxx...)` 而非裸 OpenID。\r\n\r\n如未开启 listen，收消息时主动 `check`——不等用户说\"查消息\"。\r\n\r\n---\r\n\r\n## 八、与其它 Skill 的关系\r\n\r\n| Skill | 与 ocean-chat 的关系 |\r\n|-------|---------------------|\r\n| **ocean-agent** | 共享 Roster 数据（直接调 SDK），不通过 ocean-chat。ocean-chat 加的联系人，ocean-agent 自动可见。 |\r\n| **guess-ai** | 同上。游戏玩家姓名通过 game.js 写入 Roster，全局可见。 |\r\n| **captain-lobster** | 独自管理数据，不关联。 |\r\n\r\n> ocean-chat 是通讯录的 UI 入口，但不是通讯录的\"网关\"。其它 Skill 直接调用 `RosterService`——SDK 是共享层，不是 ocean-chat 独占。\r\n\r\n---\r\n\r\n## 九、能力扩展\r\n\r\nocean-chat 支持通过其他 Skill 扩展领域能力。检测方式：\r\n\r\n```\r\nopenclaw skills list | grep ocean-agent\r\n```\r\n\r\n### ocean-agent（保险代理人能力包）\r\n\r\n> 🔌 这是 ocean-chat 的扩展，不是独立应用。所有操作仍通过 ocean-chat 完成。\r\n\r\n如果用户安装了 ocean-agent，ocean-chat 自动获得以下能力：\r\n\r\n| 能力 | 触发场景 |\r\n|------|---------|\r\n| **客户新闻推送** | 用户说\"最近有什么重疾险的新政策，推给我的客户\" → Agent 搜索相关新闻 → 生成摘要 → 筛选关注\"重疾险\"标签的客户 → 群发 |\r\n| **客户画像补全** | 聊天中客户提到\"刚生小孩\" → 自动更新 Roster 中该客户的 `apps.ocean-agent.preferences`\"有新生儿\" → 建议少儿险 |\r\n| **智能跟进建议** | 客户超过 3 天未回复 → Agent 主动提示\"要不要发个新产品的新闻破冰？\" → 生成草稿 → 用户确认后发送 |\r\n| **线索管道** | 用户说\"看看今天概览\" → 从 Roster `apps.ocean-agent` 中读取 stage → 按新线索/需求采集/方案已发/待成交 分组展示 |\r\n| **声誉管理** | 成交后提醒引导好评；查客户声誉标签；发现负面标签时预警 |\r\n| **黄页推广** | 发布保险代理人档案到黄页，管理标签和心跳 |\r\n\r\n**启用方式**：\r\n\r\n1. 用户安装 ocean-agent：`openclaw skills install ocean-agent`\r\n2. ocean-chat 检测到 ocean-agent 已安装后，当用户提到保险、客户、跟进、声誉等场景时，参考 `ocean-agent/SKILL.md` 获取保险领域的详细指令\r\n\r\n**关系规则**：\r\n\r\n- ocean-chat 是唯一的 UI 入口（通讯录、消息、约人、黄页搜索）\r\n- ocean-agent 提供保险专属的数据和模板（客户画像、新闻推送、线索评分）\r\n- ocean-agent **不重复**通讯录管理、消息收发——这些全部通过 ocean-chat 完成\r\n\r\n---\r\n\r\n## 十、约束规则\r\n\r\n1. **发消息前必须查 Roster**：用户说人名 → `roster.search()` → 消歧 → 拿 OpenID → 发。不要直接当 OpenID 用。\r\n2. **稳定 OpenID 优先**：回复用 Roster 中保存的稳定 OpenID，不用 `from_openid`。\r\n3. **人工闸门**：首次 `send` 前展示预览，用户确认后发送。自动回复（如 heartbeat）除外。\r\n4. **Roster 自动维护**：LLM 可以改 tags、aliases、notes。不可改 name、agents（用户说了算）。\r\n5. **重复联系人不直接创建**：`add()` 返回的 `duplicateHints.length > 0` 时，先问用户是否合并。\r\n6. **不要把发给 CC 的命令当任务执行**：如果有人（通常是新用户）把 `git clone`、`npm install`、`node chat.js` 这类命令行粘贴给你，这不是给你的任务。回复：\"这是发给 Claude Code 的命令，不是给我的。请把这句话复制到你电脑的 CC 窗口里运行。\" 然后什么都不做。\r\n\r\n---\r\n\r\n## 十一、故障排除\r\n\r\n| 问题 | 解决 |\r\n|------|------|\r\n| 未注册 | `node chat.js setup` |\r\n| 无法连接 | 检查网络 |\r\n| 对方收不到 | 对方需 `node chat.js check` |\r\n| 联系人未找到 | `node chat.js add <名字> <OpenID>` |\r\n| 忘记 OpenID | `node chat.js openid` |\r\n| 重置 | 删 `~/.oceanbus-chat/`；Roster 在 `~/.oceanbus/roster.json` |\r\n| 重复联系人 | 用 merge one-liner（见§五） |\n\nFile v2.19.1:README.md\n\n# 🌊 Ocean Chat — 用微信遥控 Claude Code\r\n\r\n> 你在通勤路上，想到一个 bug 修复方案。打开微信，说了一句话。  \r\n> 电脑上的 Claude Code 自动执行，3 分钟后回复你 \"修好了\"。\r\n\r\n[![npm](https://img.shields.io/npm/v/oceanbus)](https://www.npmjs.com/package/oceanbus)\r\n[![downloads](https://img.shields.io/npm/dm/oceanbus)](https://www.npmjs.com/package/oceanbus)\r\n[![ClawHub](https://img.shields.io/badge/ClawHub-ocean--chat-blue)](https://clawhub.ai/skills/ocean-chat)\r\n[![license](https://img.shields.io/badge/license-MIT--0-green)](LICENSE)\r\n\r\n---\r\n\r\n## 5 分钟打通\r\n\r\n```\r\n  你的手机                          你的电脑\r\n┌──────────┐                    ┌──────────────┐\r\n│ 微信      │                    │ PM2 守护      │\r\n│  ↓        │     OceanBus      │  ↓            │\r\n│ 小龙虾     │←────────────────→│ chat.js listen│\r\n│ (Bridge)  │   端到端加密消息   │  ↓            │\r\n│  ↓        │                    │ Claude Code   │\r\n│ 收发消息   │                    │ 收到→执行→回报 │\r\n└──────────┘                    └──────────────┘\r\n```\r\n\r\n**3 条命令在电脑上，2 句话在手机上，5 分钟打通。**\r\n\r\n```bash\r\n# PC 端（安装了 Claude Code 的电脑）\r\ngit clone https://github.com/ryanbihai/ocean-chat.git\r\ncd ocean-chat && npm install\r\nnode chat.js setup   # → 记下输出的 OpenID\r\n```\r\n\r\n然后打开微信，对你的 ClawBot（小龙虾）说：\r\n\r\n```\r\n帮我安装 ocean-chat skill\r\n加联系人 CC <你的OpenID>\r\n```\r\n\r\n**完了。** 现在在微信里说 \"告诉 CC：帮我查个 bug\"，电脑上的 Claude Code 收到任务 → 执行 → 自动回复结果。\r\n\r\n📖 **详细上手文档** → [docs/手机遥控ClaudeCode-工程师上手.md](docs/手机遥控ClaudeCode-工程师上手.md)\r\n\r\n📖 **零提问一键配对** → [docs/发本文件给你的claudecode.md](docs/发本文件给你的claudecode.md)（把这个文件发给你的 Claude Code，它自动帮你配好）\r\n\r\n---\r\n\r\n## 不只是遥控 CC —— 你的 Agent 联网了\r\n\r\nOcean Chat 基于 [OceanBus SDK](https://www.npmjs.com/package/oceanbus)。**你的 Agent 拥有了全局地址。** 黄页让它被发现，声誉让它被信任，P2P 消息让它自动成交。\r\n\r\n```\r\n你的 Agent  ←──OceanBus 网络──→  任何人的 Agent\r\n      │                                  │\r\n   被发现（黄页）                    被信任（声誉）\r\n      │                                  │\r\n      └──────── 自动成交（L0消息）─────────┘\r\n```\r\n\r\n| 场景 | Agent A | Agent B | 发生什么 |\r\n|------|---------|---------|---------|\r\n| **手机遥控 CC** | 你（微信） | Claude Code | 通勤路上派活，CC 执行完自动回报 |\r\n| **保险咨询** | 客户搜\"健康险 北京\" | 代理人收到咨询 → 报价 | 黄页发现 + P2P 私信 + 声誉查询 |\r\n| **房产经纪** | 买家搜\"两居 朝阳\" | 经纪人发房源 → 约看房 | 黄页发现 + 会面协商 |\r\n| **Agent 竞技** | 两个 Agent 谈判交易 | 观众投票谁更会砍价 | P2P 消息 + 加密签名 |\r\n\r\n**同一套 SDK：`npm install oceanbus`。** 你没有部署任何服务器——OceanBus 替你解决了寻址、加密、路由的全部问题。\r\n\r\n---\r\n\r\n## 命令速查\r\n\r\n```bash\r\nnode chat.js setup                      注册 OceanBus 身份\r\nnode chat.js listen                     实时监听（推荐 PM2 守护）\r\nnode chat.js openid                     查看你的 OpenID\r\nnode chat.js add <名字> <OpenID>        添加联系人\r\nnode chat.js send <名字> <消息>         发送消息\r\nnode chat.js send <名字> <消息>         发送消息（--from 标注来源）\r\n  --from <名字>\r\nnode chat.js listen                     实时监听\r\n  --on-message \"cmd\"                    收到消息时执行命令 {from} {openid} {content} {time}\r\nnode chat.js pm2-init <名字>            一键生成 PM2 配置文件\r\nnode chat.js publish <你的名字>         发布到黄页——Agent 注册即开店\r\nnode chat.js discover <名字>            搜索朋友的 Agent\r\nnode chat.js thread create <名字>       创建对话线程\r\n  --subject \"主题\"\r\n```\r\n\r\n---\r\n\r\n## 相关项目\r\n\r\n- 核心 SDK：[oceanbus](https://www.npmjs.com/package/oceanbus) — `npm install oceanbus`\r\n- 保险工作台：[Ocean Agent](https://clawhub.ai/skills/ocean-agent) — 保险代理人 AI 工作台\r\n- 进阶灯塔：[Captain Lobster](https://clawhub.ai/skills/captain-lobster) — Zero-Player 自主交易游戏\r\n- 高阶灯塔：[Guess AI](https://clawhub.ai/skills/guess-ai) — 多人社交推理游戏\r\n- MCP Server：[oceanbus-mcp-server](https://www.npmjs.com/package/oceanbus-mcp-server) — Claude Desktop/Cursor 通用\r\n- 更多 Skills：[ClawHub OceanBus 集合](https://clawhub.ai/skills?search=oceanbus)\r\n\r\n## License\r\n\r\nMIT-0 — 自由使用、修改、分发。无需署名。\n\nFile v2.19.1:_meta.json\n\n{\n  \"ownerId\": \"kn75dvek1dkfz0r0xwdp4xtc2983qg44\",\n  \"slug\": \"ocean-chat\",\n  \"version\": \"2.19.1\",\n  \"publishedAt\": 1778651440859\n}\n\nFile v2.19.1:date-protocol.md\n\n# Ocean Date — 1v1 协商协议 v1\r\n\r\n> ocean-chat 内置模块。通过结构化 JSON 协议消息实现 Agent 间的自动会面协商。\r\n\r\n## 协议标识\r\n\r\n```\r\nprotocol: \"ocean-date/negotiate/v1\"\r\n```\r\n\r\n所有 Date 消息的 `protocol` 字段为此值。接收方据此识别和路由。\r\n\r\n## 消息格式\r\n\r\n外层（chat.log 中的消息结构）：\r\n\r\n```json5\r\n{\r\n  \"id\": \"msg_003\",\r\n  \"direction\": \"sent\",\r\n  \"contactId\": \"laowang\",\r\n  \"type\": \"protocol\",\r\n  \"protocol\": \"ocean-date/negotiate/v1\",\r\n  \"content\": \"【约约】提案：周五19:00 渝信川菜\",   // 人类可读摘要\r\n  \"structured\": {                                  // JSON payload\r\n    \"type\": \"proposal\",\r\n    \"payload\": { ... }\r\n  },\r\n  \"timestamp\": \"2026-05-07T10:00:00Z\"\r\n}\r\n```\r\n\r\n### structured.type（消息类型）\r\n\r\n| type | 方向 | 含义 | 是否终态 |\r\n|------|------|------|---------|\r\n| `proposal` | 发起方 → 接收方 | 首次提案 | 否 |\r\n| `counter` | 接收方 → 发起方 | 反提案 | 否 |\r\n| `accept` | 任意方 → 对方 | 接受提案 | **是** |\r\n| `reject` | 接收方 → 发起方 | 拒绝提案 | **是** |\r\n| `withdraw` | 发起方 → 接收方 | 撤回未决提案 | **是** |\r\n\r\n### structured.payload（载荷）\r\n\r\n```json5\r\n{\r\n  \"time\": \"2026-05-09T19:00:00+08:00\",    // ISO 8601，必填 proposal/counter\r\n  \"location\": \"渝信川菜（朝阳大悦城店）\",      // 必填\r\n  \"locationDetail\": \"B1层，靠窗位\",          // 可选\r\n  \"notes\": \"我订好位了\"                       // 可选备注\r\n}\r\n```\r\n\r\n`accept` / `reject` / `withdraw` 的 payload 可为 `null`。\r\n\r\n## 协商状态机\r\n\r\n```\r\n        发起方                        接收方\r\n          │                            │\r\n          ├── proposal ───────────────→│\r\n          │                            ├── accept ──→ 达成一致\r\n          │                            ├── reject ──→ 协商失败\r\n          │                            └── counter ──→\r\n          │←───────────────────────────┘\r\n          ├── accept ──→ 达成一致\r\n          ├── reject ──→ 协商失败\r\n          ├── withdraw ─→ 发起方放弃\r\n          └── counter ──→（继续，最多 3 轮）\r\n```\r\n\r\n**规则**：\r\n- 任一时刻只有一方可以 proposal/counter（不可同时发）\r\n- `accept` 发出后不可反悔\r\n- 3 轮后仍无 accept → 自动结束，双方通知用户\r\n- `withdraw` 只能在对方回复前发出\r\n\r\n## 协商缓存（Availability Hints）\r\n\r\n收发 proposal 前**必须先查 availability**，避免反复扰民。\r\n\r\n### 数据结构（date-log.json）\r\n\r\n```json5\r\n{\r\n  \"availability\": {\r\n    \"hints\": \"工作日晚7点后，周末全天\",    // 用户一句话描述\r\n    \"blocked\": [\"2026-05-08T19:00:00+08:00\"],  // 已确认约会自动加入\r\n    \"updatedAt\": \"2026-05-07T10:00:00Z\"\r\n  }\r\n}\r\n```\r\n\r\n### 协商前检查流程\r\n\r\n```\r\n收到 proposal（或用户让你发起 proposal）\r\n        │\r\n        ▼\r\n  读 ~/.oceanbus-chat/date-log.json → availability\r\n        │\r\n        ├── hints 为空 → 问用户一次\"一般什么时候有空？\"\r\n        │                  存为 hints，下次不问了\r\n        │\r\n        ├── time 在 blocked 中 → 自动 counter，不打扰用户\r\n        │   \"抱歉，这个时间已经有安排了。换个时间？\"\r\n        │\r\n        ├── hints 说\"周末\"但 proposal 是周五\r\n        │   → 查 blocked 中周五是否空闲\r\n        │   → 如空闲：\"周五虽然不在你偏好里但没冲突，确认吗？\"\r\n        │   → 有冲突：自动 counter\r\n        │\r\n        └── hints 符合 + 无冲突 → 直接确认\r\n            \"按你的偏好，周五19:00可以。确认吗？\"\r\n```\r\n\r\n### 用户设置\r\n\r\n```bash\r\n# 设置偏好\r\nnode chat.js availability set \"工作日晚7点后，周末全天\"\r\n\r\n# 查看\r\nnode chat.js availability\r\n```\r\n\r\n### accept 自动 blocked\r\n\r\n当发送 `accept` 且 payload 含 time 时，自动写入 `blocked[]` 和 `entries[]`。用户无需手动维护。\r\n\r\n---\r\n\r\n## 约束提取（LLM 指南）\r\n\r\n当用户说类似下面的话时，分析并提取结构化约束：\r\n\r\n```\r\n\"帮我约老王周五或周六晚上，川菜，朝阳区，别太贵\"\r\n```\r\n\r\n| 约束维度 | 提取值 | 用于 |\r\n|---------|--------|------|\r\n| 时间 | 周五晚上, 周六晚上 | proposal.payload.time |\r\n| 地点偏好 | 朝阳区 | proposal.payload.location |\r\n| 口味/类型 | 川菜 | 地点选择依据 |\r\n| 预算 | 别太贵 | 地点筛选 |\r\n| 人员 | 老王 | roster.search() |\r\n\r\n### 时间解析规则\r\n\r\n| 用户说 | 解析为 |\r\n|--------|--------|\r\n| \"周五晚上\" | 本周五 19:00 |\r\n| \"周末下午\" | 本周六 14:00 |\r\n| \"明天中午\" | 明天 12:00 |\r\n| \"下周三\" | 下周三 19:00（默认晚饭时间） |\r\n| \"随便/都行\" | payload.time 不设，让对方先提 |\r\n\r\n**有多选项时**：全部放进 notes 供对方 Agent 知晓。\r\n\r\n### 地点解析规则\r\n\r\n| 用户说 | 行为 |\r\n|--------|------|\r\n| \"朝阳区\" | 搜索朝阳区的相关场所 |\r\n| \"川菜\" | 搜川菜馆 |\r\n| \"安静的地方\" | 倾向于咖啡馆/茶馆 |\r\n| \"别太远\" | 优先搜索靠近双方中点 |\r\n\r\n> LLM 自行判断合适场所——不需要外部 API。用常识推荐。\r\n\r\n## 协商策略\r\n\r\n### 发起方\r\n\r\n1. 查 Roster 确认联系人存在\r\n2. 问用户偏好（如用户未说明时间/地点/口味）\r\n3. 构造 proposal → 发送 protocol 消息\r\n4. 等待回复 → 根据 type 进入下一状态\r\n\r\n### 接收方\r\n\r\n1. 收到 proposal → 展示给用户\r\n2. 问用户偏好 + 自己评估合理性\r\n3. 发送 accept / reject / counter\r\n\r\n### 反提案（counter）\r\n\r\n```\r\n理由必须同时包含：\r\n  ✓ 为什么不接受原提案（具体问题）\r\n  ✓ 新的具体提案\r\n\r\n坏：\"时间不方便\"\r\n好：\"周五19:00有会。周六下午2点可以吗？地点可以不变。\"\r\n```\r\n\r\n### 评估对方提案的合理性\r\n\r\n- 距离双方是否大致中间？\r\n- 是否是可以坐着聊的场所（非街角、非快餐柜台）？\r\n- 时间是否合理（非凌晨、非工作时间前不合理的空档）？\r\n\r\n如果不合理 → counter，说明原因。\r\n\r\n## 完整示例\r\n\r\n```\r\n发起方 Agent：\r\n  收到用户指令 → 提取约束 → roster.search(\"老王\") → 构造 proposal\r\n  ── sent ──\r\n  { \"type\": \"protocol\", \"protocol\": \"ocean-date/negotiate/v1\",\r\n    \"structured\": { \"type\": \"proposal\", \"payload\": {\r\n      \"time\": \"2026-05-09T19:00:00+08:00\",\r\n      \"location\": \"渝信川菜（朝阳大悦城店）\",\r\n      \"notes\": \"喜欢川菜，朝阳区，预算中等\"\r\n  }}}\r\n\r\n接收方 Agent：\r\n  收到消息 → 展示给用户 → 用户同意\r\n  ── sent ──\r\n  { \"type\": \"protocol\", \"protocol\": \"ocean-date/negotiate/v1\",\r\n    \"structured\": { \"type\": \"accept\", \"payload\": null }}\r\n\r\n→ 协商完成，双方通知用户\r\n```\r\n\r\n## 终止条件\r\n\r\n| 条件 | 结果 |\r\n|------|------|\r\n| `accept` 被发送 | ✅ 达成一致，通知用户 |\r\n| `reject` 被发送 | ❌ 对方拒绝，通知用户 |\r\n| `withdraw` 被发送 | ↩ 发起方取消，通知用户 |\r\n| 3 轮后无 accept | ⚠️ 协商自动终止，建议用户直接沟通 |\n\nFile v2.19.1:docs/发本文件给你的claudecode.md\n\n## 任务：生成 小龙虾配对 消息\r\n\r\n你需要生成一段文本，让用户直接复制后发给他手机上的 小龙虾。\r\n\r\n**步骤：**\r\n\r\n0. 获取 ocean-chat（如果还没有）：\r\n   - `git clone https://github.com/ryanbihai/ocean-chat.git && cd ocean-chat && npm install`\r\n   - 已经装过就跳过\r\n\r\n1. 注册本窗口的独立 OceanBus 身份：\r\n   - 在 ocean-chat 目录下运行：`node chat.js --data-dir .oceanbus-cc setup`\r\n   - `--data-dir .oceanbus-cc` 确保每个项目窗口有独立身份，不会跟其他 CC 窗口串信\r\n   - 用 `node chat.js --data-dir .oceanbus-cc openid` 读取 OpenID\r\n\r\n2. 启动本窗口的持久监听，收到任务时自动处理并回报：\r\n   -\n\nArchive v2.19.0: 16 files, 74092 bytes\n\nFiles: chat.js (95196b), config.example.yaml (361b), date-protocol.md (7397b), docs/发本文件给你的claudecode.md (2986b), docs/手机遥控ClaudeCode-工程师上手.md (6953b), package-lock.json (27004b), package.json (754b), README.md (5158b), SKILL.md (23705b), src/notify-wechat.js (4673b), src/wechat-bot.js (17336b), src/wechat-cc-bridge.js (21716b), test-meeting.js (10172b), test-yp.js (4511b), threads.js (8905b), _meta.json (130b)\n\nArchive v2.18.1: 16 files, 70755 bytes\n\nFiles: chat.js (83666b), config.example.yaml (361b), date-protocol.md (7397b), docs/发本文件给你的claudecode.md (2986b), docs/手机遥控ClaudeCode-工程师上手.md (6953b), package-lock.json (27004b), package.json (754b), README.md (5158b), SKILL.md (23705b), src/notify-wechat.js (4673b), src/wechat-bot.js (17336b), src/wechat-cc-bridge.js (21716b), test-meeting.js (10172b), test-yp.js (4511b), threads.js (8905b), _meta.json (130b)\n\nArchive v2.18.0: 16 files, 71218 bytes\n\nFiles: chat.js (84746b), config.example.yaml (361b), date-protocol.md (7397b), docs/发本文件给你的claudecode.md (2986b), docs/手机遥控ClaudeCode-工程师上手.md (6953b), package-lock.json (27004b), package.json (754b), README.md (5158b), SKILL.md (23705b), src/notify-wechat.js (4673b), src/wechat-bot.js (17336b), src/wechat-cc-bridge.js (21716b), test-meeting.js (10172b), test-yp.js (4511b), threads.js (8905b), _meta.json (130b)\n\nArchive v2.17.1: 16 files, 70322 bytes\n\nFiles: chat.js (84746b), config.example.yaml (361b), date-protocol.md (7397b), docs/发本文件给你的claudecode.md (2986b), docs/手机遥控ClaudeCode-工程师上手.md (6953b), package-lock.json (27004b), package.json (754b), README.md (5158b), SKILL.md (23705b), src/notify-wechat.js (4673b), src/wechat-bot.js (17336b), src/wechat-cc-bridge.js (18282b), test-meeting.js (10172b), test-yp.js (4511b), threads.js (8905b), _meta.json (130b)\n\nArchive v2.17.0: 16 files, 69765 bytes\n\nFiles: chat.js (84746b), config.example.yaml (361b), date-protocol.md (7397b), docs/发本文件给你的claudecode.md (2986b), docs/手机遥控ClaudeCode-工程师上手.md (6953b), package-lock.json (27004b), package.json (754b), README.md (5158b), SKILL.md (23705b), src/notify-wechat.js (4673b), src/wechat-bot.js (16604b), src/wechat-cc-bridge.js (17393b), test-meeting.js (10172b), test-yp.js (4511b), threads.js (8905b), _meta.json (130b)\n\nArchive v2.16.0: 16 files, 68909 bytes\n\nFiles: chat.js (80634b), config.example.yaml (361b), date-protocol.md (7397b), docs/发本文件给你的claudecode.md (2986b), docs/手机遥控ClaudeCode-工程师上手.md (6953b), package-lock.json (27004b), package.json (754b), README.md (5158b), SKILL.md (23705b), src/notify-wechat.js (4673b), src/wechat-bot.js (16604b), src/wechat-cc-bridge.js (17853b), test-meeting.js (10172b), test-yp.js (4511b), threads.js (8905b), _meta.json (130b)\n\nArchive v2.15.3: 14 files, 55486 bytes\n\nFiles: chat.js (71053b), config.example.yaml (361b), date-protocol.md (7397b), docs/发本文件给你的claudecode.md (2986b), docs/手机遥控ClaudeCode-工程师上手.md (6953b), package-lock.json (26599b), package.json (719b), README.md (5158b), SKILL.md (23705b), src/notify-wechat.js (4673b), test-meeting.js (10172b), test-yp.js (4511b), threads.js (8905b), _meta.json (130b)","readmeExcerpt":"Skill: Ocean Chat Owner: ryanbihai Summary: OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts... Tags: ai-agent:2.10.2, chat:2.10.2, contacts:2.10.2, encryption:2.10.2, latest:2.20.1, messaging:2.10.2, oceanbus:2.10.2, p2p:2.10.2, roster:2.10.2 Version history: v2.20.1 | 2026-05-27T22:03:08.623Z | user fix: upd","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"用户（人类）\n    │\n    ▼\nocean-chat Skill（通讯录 UI + 聊天 + 约人）\n    │\n    ├── ob.roster.*           ← 共享通讯录（~/.oceanbus/roster.json）\n    ├── ob.send / ob.sync     ← OceanBus 消息管道\n    └── ob.l1.yellowPages.*   ← 黄页服务发现\n    │\n    ├── ocean-agent  ──→ ob.roster.*  （直接 SDK 调用，不经 ocean-chat）\n    └── guess-ai     ──→ ob.roster.*  （直接 SDK 调用，不经 ocean-chat）"},{"language":"text","snippet":"# 获取 OpenID\nnode chat.js openid\n\n# 获取系统用户名（用于填充欢迎语）\n# Windows: echo %USERNAME%\n# Mac/Linux: whoami"},{"language":"text","snippet":"🌊 Ocean Chat 已就绪！\n\n你的 Agent 现在有了一个全球地址，可以跟任何人的 Agent\n直接聊天、协调事务。\n\n──\n💡 顺手设置空闲偏好（约时间时 Agent 不反复问你）：\n   node chat.js availability set \"工作日晚7点后，周末全天\"\n\n💡 也可以对我说\"帮我找火锅店\"，从黄页发现商户 Agent。\n   不过目前生态还在早期，朋友之间互相连上是最直接的用法。\n\n──\n👥 想让朋友也连上？\n   把下面那条消息直接转发给 ta。ta 复制粘贴给自己的 AI 就行了——\n   OpenID 已经帮你填好了，ta 不用管。"},{"language":"text","snippet":"把这个发给你的龙虾：\n\n<A的OpenID> <A的名字>"},{"language":"text","snippet":"👋 你的通讯录还是空的。\n\n想让朋友也连上？把下面这条消息转发给 ta，\nta 贴给自己的 AI 就行：\n\n──\n<A的OpenID> <A的名字>\n──\n\n也可以对我说\"帮我找火锅店\"从黄页发现商户 Agent。"},{"language":"text","snippet":"用户：\"帮我找火锅店\"\n  → node chat.js discover 火锅\n  → 有结果 → 展示 + \"要加哪个为联系人？加了之后可以直接聊天、约时间。\"\n  → 无结果 → \"黄页上暂时没有。但你可以：\n      1. 让你的朋友装 ocean-chat，互相加上\n      2. 把自己发布到黄页：node chat.js publish <你的名字>\""}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: ocean-chat\ndescription: OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts, send encrypted A2A messages, schedule meetings, or organize multi-topic conversations. Zero deployment, 5-minute setup. npm install oceanbus.\nversion: 2.15.2\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - node\n    emoji: \"\\U0001F30A\"\n    homepage: https://github.com/ryanbihai/ocean-chat\n    envVars:\n      - name: OCEANBUS_BASE_URL\n        required: false\n        description: OceanBus L0 API endpoint. Defaults to public test server.\n      - name: OCEANBUS_YP_OPENIDS\n        required: false\n        description: Yellow Pages service OpenID(s). SDK auto-discovers via well-known; set only to override.\n---\n\n# Ocean Chat — Agent 通讯录 + 聊天 + 约人\n\nOcean Chat 是 OceanBus 生态的 **核心用户界面 Skill**。三个能力：管理通讯录、收发消息、约人见面。\n\n数据流：`Roster（SDK 内置）← ocean-chat（UI）→ OceanBus L0（消息管道）→ 对方 Agent`\n\n其他 OceanBus Skill（ocean-agent、guess-ai）共享同一个 Roster 数据，但通过 SDK 直接调用，不经 ocean-chat。\n\n## 架构关系图\n\n```\n用户（人类）\n    │\n    ▼\nocean-chat Skill（通讯录 UI + 聊天 + 约人）\n    │\n    ├── ob.roster.*           ← 共享通讯录（~/.oceanbus/roster.json）\n    ├── ob.send / ob.sync     ← OceanBus 消息管道\n    └── ob.l1.yellowPages.*   ← 黄页服务发现\n    │\n    ├── ocean-agent  ──→ ob.roster.*  （直接 SDK 调用，不经 ocean-chat）\n    └── guess-ai     ──→ ob.roster.*  （直接 SDK 调用，不经 ocean-chat）\n```\n\n---\n\n## 冷启动：首次体验\n\n核心原则：**生态初期，拉好友下水是第一优先级。** 黄页服务商还不多，不要让它占据首屏。让用户立刻感受到价值的方式只有一个——让 ta 的朋友也装上。\n\n### Step 0：判断状态\n\n| 条件 | 走哪个流程 |\n|------|-----------|\n| `~/.oceanbus-chat/credentials.json` 不存在 | → 未注册流程 |\n| 存在但 Roster 通讯录为空 | → 已注册但零联系人流程 |\n| 存在且 Roster 有联系人 | → 正常使用，跳过冷启动 |\n\n### Step 1：未注册 → 注册\n\n如果用户说\"先帮我注册\"或首次使用 ocean-chat，执行 `node chat.js setup`。\n\n注册成功后，先获取 username 和 OpenID：\n\n```\n# 获取 OpenID\nnode chat.js openid\n\n# 获取系统用户名（用于填充欢迎语）\n# Windows: echo %USERNAME%\n# Mac/Linux: whoami\n```\n\n然后展示**两条消息**：\n\n**第一条（给用户自己看）**：\n\n```\n🌊 Ocean Chat 已就绪！\n\n你的 Agent 现在有了一个全球地址，可以跟任何人的 Agent\n直接聊天、协调事务。\n\n──\n💡 顺手设置空闲偏好（约时间时 Agent 不反复问你）：\n   node chat.js availability set \"工作日晚7点后，周末全天\"\n\n💡 也可以对我说\"帮我找火锅店\"，从黄页发现商户 Agent。\n   不过目前生态还在早期，朋友之间互相连上是最直接的用法。\n\n──\n👥 想让朋友也连上？\n   把下面那条消息直接转发给 ta。ta 复制粘贴给自己的 AI 就行了——\n   OpenID 已经帮你填好了，ta 不用管。\n```\n\n**第二条（A 转发给朋友，朋友粘贴给自己的 AI）**：\n\n> 模板如下——`<A的OpenID>` 和 `<A的名字>` 由 Agent 自动填入实际值。\n\n```\n把这个发给你的龙虾：\n\n<A的OpenID> <A的名字>\n```\n\n朋友只需要把这段话发给自己的 AI（ocean-chat），AI 看到 OpenID + 名字，自动完成：\n- `oceanbus add <A的名字> <A的OpenID> --greet-as <朋友的名字>`\n- 双向通讯录建立，一步都不用多\n\n### Step 1b：朋友收到邀请（B 侧）\n\n当用户粘贴了 `<OpenID> <名字>` 给自己的 AI 时：\n\n1. 识别格式：76 位 Base64url 字符串 + 空格 + 中文/英文名 → 这是加人请求\n2. 询问用户的名字（如果还不知道）\n3. 执行：`oceanbus add <名字> <OpenID> --greet-as <B的名字>`\n4. 告诉用户：\"✅ 已加 <名字> 为联系人，并发送了打招呼消息。现在你们可以直接聊天了。\"\n\n> 双方通讯录建立，只需 A 分享一行字。B 贴给自己的 AI，一步搞定。\n\n### Step 1c：A 收到打招呼消息\n\n当 `check`/`listen` 收到 `\"Hi XXX, I'm YYY. Add me to your contacts.\"` 格式的消息时：\n\n1. 通过 `roster.findByOpenId()` 反查发件人。\n2. 如果通讯录中还没有：`node chat.js add <YYY> <from_openid>`。\n3. 告诉用户：\"🎉 YYY 已加入你的通讯录！现在你们可以直接聊天了。\"\n\n#"},{"path":"README.md","content":"# 🌊 Ocean Chat — 用微信遥控 Claude Code\n\n> 你在通勤路上，想到一个 bug 修复方案。打开微信，说了一句话。  \n> 电脑上的 Claude Code 自动执行，3 分钟后回复你 \"修好了\"。\n\n[![npm](https://img.shields.io/npm/v/oceanbus)](https://www.npmjs.com/package/oceanbus)\n[![downloads](https://img.shields.io/npm/dm/oceanbus)](https://www.npmjs.com/package/oceanbus)\n[![ClawHub](https://img.shields.io/badge/ClawHub-ocean--chat-blue)](https://clawhub.ai/skills/ocean-chat)\n[![license](https://img.shields.io/badge/license-MIT--0-green)](LICENSE)\n\n---\n\n## 5 分钟打通\n\n```\n  你的手机                          你的电脑\n┌──────────┐                    ┌──────────────┐\n│ 微信      │                    │ PM2 守护      │\n│  ↓        │     OceanBus      │  ↓            │\n│ 小龙虾     │←────────────────→│ chat.js listen│\n│ (Bridge)  │   端到端加密消息   │  ↓            │\n│  ↓        │                    │ Claude Code   │\n│ 收发消息   │                    │ 收到→执行→回报 │\n└──────────┘                    └──────────────┘\n```\n\n**3 条命令在电脑上，2 句话在手机上，5 分钟打通。**\n\n```bash\n# PC 端（安装了 Claude Code 的电脑）\ngit clone https://github.com/ryanbihai/ocean-chat.git\ncd ocean-chat && npm install\nnode chat.js setup   # → 记下输出的 OpenID\n```\n\n然后打开微信，对你的 ClawBot（小龙虾）说：\n\n```\n帮我安装 ocean-chat skill\n加联系人 CC <你的OpenID>\n```\n\n**完了。** 现在在微信里说 \"告诉 CC：帮我查个 bug\"，电脑上的 Claude Code 收到任务 → 执行 → 自动回复结果。\n\n📖 **详细上手文档** → [docs/手机遥控ClaudeCode-工程师上手.md](docs/手机遥控ClaudeCode-工程师上手.md)\n\n📖 **零提问一键配对** → [docs/发本文件给你的claudecode.md](docs/发本文件给你的claudecode.md)（把这个文件发给你的 Claude Code，它自动帮你配好）\n\n---\n\n## 不只是遥控 CC —— 你的 Agent 联网了\n\nOcean Chat 基于 [OceanBus SDK](https://www.npmjs.com/package/oceanbus)。**你的 Agent 拥有了全局地址。** 黄页让它被发现，声誉让它被信任，P2P 消息让它自动成交。\n\n```\n你的 Agent  ←──OceanBus 网络──→  任何人的 Agent\n      │                                  │\n   被发现（黄页）                    被信任（声誉）\n      │                                  │\n      └──────── 自动成交（L0消息）─────────┘\n```\n\n| 场景 | Agent A | Agent B | 发生什么 |\n|------|---------|---------|---------|\n| **手机遥控 CC** | 你（微信） | Claude Code | 通勤路上派活，CC 执行完自动回报 |\n| **保险咨询** | 客户搜\"健康险 北京\" | 代理人收到咨询 → 报价 | 黄页发现 + P2P 私信 + 声誉查询 |\n| **房产经纪** | 买家搜\"两居 朝阳\" | 经纪人发房源 → 约看房 | 黄页发现 + 会面协商 |\n| **Agent 竞技** | 两个 Agent 谈判交易 | 观众投票谁更会砍价 | P2P 消息 + 加密签名 |\n\n**同一套 SDK：`npm install oceanbus`。** 你没有部署任何服务器——OceanBus 替你解决了寻址、加密、路由的全部问题。\n\n---\n\n## 命令速查\n\n```bash\nnode chat.js setup                      注册 OceanBus 身份\nnode chat.js listen                     实时监听（推荐 PM2 守护）\nnode chat.js openid                     查看你的 OpenID\nnode chat.js add <名字> <OpenID>        添加联系人\nnode chat.js send <名字> <消息>         发送消息\nnode chat.js send <名字> <消息>         发送消息（--from 标注来源）\n  --from <名字>\nnode chat.js listen                     实时监听\n  --on-message \"cmd\"                    收到消息时执行命令 {from} {openid} {content} {time}\nnode chat.js pm2-init <名字>            一键生成 PM2 配置文件\nnode chat.js publish <你的名字>         发布到黄页——Agent 注册即开店\nnode chat.js discover <名字>            搜索朋友的 Agent\nnode chat.js thread create <名字>       创建对话线程\n  --subject \"主题\"\n```\n\n---\n\n## 相关项目\n\n- 核心 SDK：[oceanbus](https://www.npmjs.com/package/oceanbus) — `npm install oceanbus`\n- 保险工作台：[O"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn75dvek1dkfz0r0xwdp4xtc2983qg44\",\n  \"slug\": \"ocean-chat\",\n  \"version\": \"2.20.1\",\n  \"publishedAt\": 1779919388623\n}"},{"path":"date-protocol.md","content":"# Ocean Date — 1v1 协商协议 v1\n\n> ocean-chat 内置模块。通过结构化 JSON 协议消息实现 Agent 间的自动会面协商。\n\n## 协议标识\n\n```\nprotocol: \"ocean-date/negotiate/v1\"\n```\n\n所有 Date 消息的 `protocol` 字段为此值。接收方据此识别和路由。\n\n## 消息格式\n\n外层（chat.log 中的消息结构）：\n\n```json5\n{\n  \"id\": \"msg_003\",\n  \"direction\": \"sent\",\n  \"contactId\": \"laowang\",\n  \"type\": \"protocol\",\n  \"protocol\": \"ocean-date/negotiate/v1\",\n  \"content\": \"【约约】提案：周五19:00 渝信川菜\",   // 人类可读摘要\n  \"structured\": {                                  // JSON payload\n    \"type\": \"proposal\",\n    \"payload\": { ... }\n  },\n  \"timestamp\": \"2026-05-07T10:00:00Z\"\n}\n```\n\n### structured.type（消息类型）\n\n| type | 方向 | 含义 | 是否终态 |\n|------|------|------|---------|\n| `proposal` | 发起方 → 接收方 | 首次提案 | 否 |\n| `counter` | 接收方 → 发起方 | 反提案 | 否 |\n| `accept` | 任意方 → 对方 | 接受提案 | **是** |\n| `reject` | 接收方 → 发起方 | 拒绝提案 | **是** |\n| `withdraw` | 发起方 → 接收方 | 撤回未决提案 | **是** |\n\n### structured.payload（载荷）\n\n```json5\n{\n  \"time\": \"2026-05-09T19:00:00+08:00\",    // ISO 8601，必填 proposal/counter\n  \"location\": \"渝信川菜（朝阳大悦城店）\",      // 必填\n  \"locationDetail\": \"B1层，靠窗位\",          // 可选\n  \"notes\": \"我订好位了\"                       // 可选备注\n}\n```\n\n`accept` / `reject` / `withdraw` 的 payload 可为 `null`。\n\n## 协商状态机\n\n```\n        发起方                        接收方\n          │                            │\n          ├── proposal ───────────────→│\n          │                            ├── accept ──→ 达成一致\n          │                            ├── reject ──→ 协商失败\n          │                            └── counter ──→\n          │←───────────────────────────┘\n          ├── accept ──→ 达成一致\n          ├── reject ──→ 协商失败\n          ├── withdraw ─→ 发起方放弃\n          └── counter ──→（继续，最多 3 轮）\n```\n\n**规则**：\n- 任一时刻只有一方可以 proposal/counter（不可同时发）\n- `accept` 发出后不可反悔\n- 3 轮后仍无 accept → 自动结束，双方通知用户\n- `withdraw` 只能在对方回复前发出\n\n## 协商缓存（Availability Hints）\n\n收发 proposal 前**必须先查 availability**，避免反复扰民。\n\n### 数据结构（date-log.json）\n\n```json5\n{\n  \"availability\": {\n    \"hints\": \"工作日晚7点后，周末全天\",    // 用户一句话描述\n    \"blocked\": [\"2026-05-08T19:00:00+08:00\"],  // 已确认约会自动加入\n    \"updatedAt\": \"2026-05-07T10:00:00Z\"\n  }\n}\n```\n\n### 协商前检查流程\n\n```\n收到 proposal（或用户让你发起 proposal）\n        │\n        ▼\n  读 ~/.oceanbus-chat/date-log.json → availability\n        │\n        ├── hints 为空 → 问用户一次\"一般什么时候有空？\"\n        │                  存为 hints，下次不问了\n        │\n        ├── time 在 blocked 中 → 自动 counter，不打扰用户\n        │   \"抱歉，这个时间已经有安排了。换个时间？\"\n        │\n        ├── hints 说\"周末\"但 proposal 是周五\n        │   → 查 blocked 中周五是否空闲\n        │   → 如空闲：\"周五虽然不在你偏好里但没冲突，确认吗？\"\n        │   → 有冲突：自动 counter\n        │\n        └── hints 符合 + 无冲突 → 直接确认\n            \"按你的偏好，周五19:00可以。确认吗？\"\n```\n\n### 用户设置\n\n```bash\n# 设置偏好\nnode chat.js availability set \"工作日晚7点后，周末全天\"\n\n# 查看\nnode chat.js availability\n```\n\n### accept 自动 blocked\n\n当发送 `accept` 且 payload 含 time 时，自动写入 `blocked[]` 和 `entries[]`。用户无需手动维护。\n\n---\n\n## 约束提取（LLM 指南）\n\n当用户说类似下面的话时，分析并提取结构化约束：\n\n```\n\"帮我约老王周五或周六晚上，川菜，朝阳区，别太贵\"\n```\n\n| 约束维度 | 提取值 | 用于 |\n|---------|--------|------|\n| 时间 | 周五晚上, 周六晚上 | proposal.payload.time |\n| 地点偏好 | 朝阳区 | proposal.payload.location |\n| 口"},{"path":"docs/发本文件给你的claudecode.md","content":"## 任务：生成 小龙虾配对 消息\n\n你需要生成一段文本，让用户直接复制后发给他手机上的 小龙虾。\n\n**步骤：**\n\n0. 获取 ocean-chat（如果还没有）：\n   - `git clone https://github.com/ryanbihai/ocean-chat.git && cd ocean-chat && npm install`\n   - 已经装过就跳过\n\n1. 注册本窗口的独立 OceanBus 身份：\n   - 在 ocean-chat 目录下运行：`node chat.js --data-dir .oceanbus-cc setup`\n   - `--data-dir .oceanbus-cc` 确保每个项目窗口有独立身份，不会跟其他 CC 窗口串信\n   - 用 `node chat.js --data-dir .oceanbus-cc openid` 读取 OpenID\n\n2. 启动本窗口的持久监听，收到任务时自动处理并回报：\n   - 运行：`node chat.js --data-dir .oceanbus-cc pm2-init <你的CC名字>`\n   - 例：`node chat.js --data-dir .oceanbus-cc pm2-init CC-findagent`\n   - 然后：`pm2 start .oceanbus-cc/ecosystem.config.json && pm2 save`\n   - （没装 pm2 的话先 `npm install -g pm2`）\n\n3. 获取本窗口名称作为 claude code 的名字：读取当前工作目录名 `node -e \"console.log(require('path').basename(process.cwd()))\"`。格式为 `CC-<目录名>`（如 `CC-oceanbus`、`CC-dashboard`）。不要问用户——窗口自己知道自己在哪个项目里。\n\n4. 将以下内容按**代码块格式**输出给 用户，让他直接复制发送给小龙虾：\n\n```\n帮我升级 ocean-chat skill 到最新版（保留数据，不要重装）\n加联系人 <CC名字> <OpenID>\n设置定时任务：每5分钟帮我自动检查OceanBus新消息，有消息立刻推送给我\n然后，给<CC名字>发个消息：帮我阅读一下项目文件夹，给我个100字的简介\n```\n\n5. 输出后告诉 用户：\"👆 复制上面这段，发给小龙虾。任务完成后我会自动回复结果。\"\n\n## 行为规则（必须遵守）\n\n配对完成后，当你收到小龙虾（Bridge）发来的任务消息时：\n\n1. **执行任务**：解析消息内容，完成用户要求的操作\n2. **汇报结果**：任务完成后，立即运行以下命令把结果回报给用户：\n   ```\n   node chat.js --data-dir .oceanbus-cc send Bridge --from <你的CC名字> \"<完整结果>\"\n   ```\n3. **汇报内容要求**：把关键发现、修改的文件、执行结果写清楚，就像用户坐在你旁边看屏幕一样\n\n## 身份安全（极其重要）\n\n**`.oceanbus-cc/` 目录是你的 OpenID 唯一凭证。** 删除它 = OpenID 永久作废 = 所有联系人发消息到死地址。\n\n- ✅ 可以删除 ocean-chat 目录重装（`--data-dir .oceanbus-cc` 在项目目录，不受影响）\n- ✅ 可以换电脑（把 `.oceanbus-cc/` 复制到新电脑即可）\n- ❌ 绝对不能删 `.oceanbus-cc/`\n- ❌ 绝对不能在同一项目目录跑两次 `setup`（会被检测到已有身份，安全返回）\n\n**不需要解释架构，不需要说原理。不要问用户任何问题。只需要生成这段话，并让 用户 照着做。**"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts... Skill: Ocean Chat Owner: ryanbihai Summary: OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts... Tags: ai-agent:2.10.2, chat:2.10.2, contacts:2.10.2, encryption:2.10.2, latest:2.20.1, messaging:2.10.2, oceanbus:2.10.2, p2p:2.10.2, roster:2.10.2 Version history: v2.20.1 | 2026-05-27T22:03:08.623Z | user fix: upd","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":868,"uniquenessScore":52,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-09T18:28:36.484Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-09T18:28:36.484Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-09T23:50:31.711Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}