@surething/cockpit 1.0.214 → 1.0.216
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next-prod/BUILD_ID +1 -1
- package/.next-prod/app-path-routes-manifest.json +3 -3
- package/.next-prod/build-manifest.json +2 -2
- package/.next-prod/prerender-manifest.json +3 -3
- package/.next-prod/server/app/_global-error/page.js.nft.json +1 -1
- package/.next-prod/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/_global-error.html +1 -1
- package/.next-prod/server/app/_global-error.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next-prod/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found/page.js.nft.json +1 -1
- package/.next-prod/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/_not-found.html +1 -1
- package/.next-prod/server/app/_not-found.rsc +3 -3
- package/.next-prod/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next-prod/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/.next-prod/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next-prod/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next-prod/server/app/api/agent/test/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/bash/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/codex/route.js +2 -2
- package/.next-prod/server/app/api/chat/codex/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/deepseek/route.js +2 -2
- package/.next-prod/server/app/api/chat/deepseek/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/kimi/route.js +2 -2
- package/.next-prod/server/app/api/chat/kimi/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/ollama/route.js +2 -2
- package/.next-prod/server/app/api/chat/ollama/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/chat/route.js +2 -2
- package/.next-prod/server/app/api/chat/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/claude-stats/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/commands/route.js +1 -1
- package/.next-prod/server/app/api/commands/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/comments/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/columns/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/export/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/db/schemas/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/dev/spans/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/extension/version/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/file/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/blame/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/clipboard/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/copy/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/delete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/expanded/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/index/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/init/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/paste/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/read/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/readdir/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/recent/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/save/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/search/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/stat/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/files/text/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/branch-diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/branches/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/commit-diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/commits/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/diff/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/discard/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/stage/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/status/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/unstage/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/git/worktree/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/global-state/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/load/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/save/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/jupyter/shutdown/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/definition/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/hover/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/references/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/status/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/lsp/warmup/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/columns/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/export/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/mysql/schemas/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/query/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/neo4j/schema/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/note/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/ollama/models/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/ollama/start/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/open-cursor/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/open-vscode/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/pick-folder/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/pinned-sessions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/project-settings/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/project-state/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/affected/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/callees/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/callers/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/coedit/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/context/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/file/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/file-functions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/impact/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/related/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/risk/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projectGraph/search/route.js +1 -1
- package/.next-prod/server/app/api/projectGraph/search/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/projects/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/command/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/connect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/delete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/disconnect/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/get/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/keys/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/redis/set/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/comments/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/replies/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/[id]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/identify/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/order/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/share-info/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/review/users/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/scheduled-tasks/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/services/config/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/services/scripts/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session/[sessionId]/fork/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session/[sessionId]/history/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/session-by-path/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/projects/[encodedPath]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/projects/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/settings/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/[id]/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/content/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/skills/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/aliases/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/autocomplete/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/bubble-order/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/env/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/terminal/history/route.js.nft.json +1 -1
- package/.next-prod/server/app/api/version/route.js.nft.json +1 -1
- package/.next-prod/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next-prod/server/app/manifest.webmanifest/route.js.nft.json +1 -1
- package/.next-prod/server/app/page.js.nft.json +1 -1
- package/.next-prod/server/app/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/project/page.js.nft.json +1 -1
- package/.next-prod/server/app/project/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app/review/[id]/page.js.nft.json +1 -1
- package/.next-prod/server/app/review/[id]/page_client-reference-manifest.js +1 -1
- package/.next-prod/server/app-paths-manifest.json +3 -3
- package/.next-prod/server/chunks/2939.js +1 -1
- package/.next-prod/server/chunks/8916.js +1 -1
- package/.next-prod/server/chunks/9658.js +3 -3
- package/.next-prod/server/chunks/{7828.js → 9877.js} +11 -2
- package/.next-prod/server/middleware-build-manifest.js +1 -1
- package/.next-prod/server/pages/404.html +1 -1
- package/.next-prod/server/pages/500.html +1 -1
- package/.next-prod/server/server-reference-manifest.json +1 -1
- package/.next-prod/static/chunks/{5188-38f55b21ae1eeb28.js → 5188-415582403ef0e29c.js} +1 -1
- package/.next-prod/static/chunks/6345-e5ceeb2aeb698eb6.js +14 -0
- package/.next-prod/static/css/{f016b445331fc5a2.css → cc6d733cdf607b30.css} +1 -1
- package/.next-prod/trace +13 -13
- package/.next-prod/trace-build +1 -1
- package/README.md +8 -7
- package/README.zh.md +8 -7
- package/bin/cock-browser.mjs +93 -3
- package/bin/cock-codegraph.mjs +907 -0
- package/bin/cock-terminal.mjs +5 -0
- package/bin/cock.mjs +4 -4
- package/bin/cockpit-dev.mjs +9 -0
- package/bin/setup-dev.mjs +92 -0
- package/package.json +3 -2
- package/.next-prod/static/chunks/6345-fc2d45a72316c5f8.js +0 -14
- package/bin/cock-affected.mjs +0 -190
- package/bin/cock-dev.mjs +0 -6
- /package/.next-prod/static/{ekMRRxcvpy2AIjMrVn4lK → GAYKr2BmQpFqJgRJfvQ3D}/_buildManifest.js +0 -0
- /package/.next-prod/static/{ekMRRxcvpy2AIjMrVn4lK → GAYKr2BmQpFqJgRJfvQ3D}/_ssgManifest.js +0 -0
package/.next-prod/trace-build
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"name":"run-webpack","duration":
|
|
1
|
+
[{"name":"run-webpack","duration":20691327,"timestamp":204749108,"id":14,"parentId":1,"tags":{},"startTime":1779801780173,"traceId":"2868cd35fd02ca16"},{"name":"run-typescript","duration":16713005,"timestamp":225450994,"id":1519,"parentId":1,"tags":{},"startTime":1779801800875,"traceId":"2868cd35fd02ca16"},{"name":"static-check","duration":1098623,"timestamp":242207653,"id":1522,"parentId":1,"tags":{},"startTime":1779801817632,"traceId":"2868cd35fd02ca16"},{"name":"static-generation","duration":6111104,"timestamp":243692459,"id":1778,"parentId":1,"tags":{},"startTime":1779801819116,"traceId":"2868cd35fd02ca16"},{"name":"collect-build-traces","duration":18808913,"timestamp":243307085,"id":1775,"parentId":1,"tags":{},"startTime":1779801818731,"traceId":"2868cd35fd02ca16"},{"name":"telemetry-flush","duration":54,"timestamp":262119910,"id":1787,"parentId":1,"tags":{},"startTime":1779801837544,"traceId":"2868cd35fd02ca16"},{"name":"next-build","duration":57539004,"timestamp":204580974,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1779801780005,"traceId":"2868cd35fd02ca16"}]
|
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ Cockpit is the instrument panel. It does **not** replace Claude Code; it stands
|
|
|
49
49
|
| Agent can't reach your browser / DB | **Smart Bubbles**: Chrome, PostgreSQL, MySQL, Redis — drivable by the agent |
|
|
50
50
|
| Reading an unfamiliar repo means a 90-min file-tree scavenger hunt | **Code Map** chip view — caller / callee pins, click to walk the call graph |
|
|
51
51
|
| Reviewing AI output is friction | **LAN-shared review pages**, line-level comments, send any comment back as AI context |
|
|
52
|
-
| Same "do X but don't change code" prompt every day | **Slash modes** `/qa /fx /
|
|
52
|
+
| Same "do X but don't change code" prompt every day | **Slash modes** `/qa /fx /ex /go /cg /cc` + custom `SKILL.md` via the Skills sidebar |
|
|
53
53
|
| No automation hooks | One-time / interval / cron-based **scheduled tasks** |
|
|
54
54
|
| "Cloud relay" trust concerns | **Fully local**. No telemetry. Keys (Codex / DeepSeek / Kimi) stay in `~/.cockpit/settings.json` on your laptop. |
|
|
55
55
|
|
|
@@ -80,7 +80,7 @@ Cockpit is the instrument panel. It does **not** replace Claude Code; it stands
|
|
|
80
80
|
- Git **blame**, diff view, branch switching, **worktree** management
|
|
81
81
|
- **LSP integration** — go to definition, find references, hover info
|
|
82
82
|
- **Code Map** — every function as a chip with caller / callee pins; click to walk the call graph. Multi-language: TS/JS, Python, Go, Rust. No LSP, no project setup, works offline.
|
|
83
|
-
- **CodeGraph** — a **code graph** for the agent: same tree-sitter index exposed as
|
|
83
|
+
- **CodeGraph** — a **code graph** for the agent: same tree-sitter index exposed as 10 HTTP endpoints — 6 base (`search` / `callers` / `callees` / `impact` / `file` / `coedit`) + 4 analytics (`context` / `related` / `risk` / `affected`, powered by PageRank · PPR · TF-IDF · Louvain, zero training) — so the agent queries coordinates instead of grepping text; trigger with `/cg` from chat. Coordinates only, never source — precise, cheap, and catches the conventional coupling no regex can.
|
|
84
84
|
- Fuzzy file search (Cmd+F), JSON viewer, Markdown preview
|
|
85
85
|
|
|
86
86
|
### Console — Terminal & smart Bubbles
|
|
@@ -103,10 +103,11 @@ Cockpit is the instrument panel. It does **not** replace Claude Code; it stands
|
|
|
103
103
|
|
|
104
104
|
- `/qa` — **Clarify-only**: restate, ask back, never code
|
|
105
105
|
- `/fx` — **Diagnose-only**: bug evidence chain, never edit
|
|
106
|
-
- `/
|
|
107
|
-
- `/
|
|
108
|
-
- `/cg` — **CodeGraph** project exploration:
|
|
109
|
-
- **
|
|
106
|
+
- `/ex` — **Explore**: 6-step structured discussion skeleton (study → diverge → converge → re-diverge → iterate-verify → summarize)
|
|
107
|
+
- `/go` — **Land**: take a converged plan, slice into MVP stages, write + self-verify per stage, recap at end
|
|
108
|
+
- `/cg` — **CodeGraph** project exploration: 10 HTTP endpoints for symbol / callers / impact / co-edit / risk / affected queries (precise where grep is fuzzy)
|
|
109
|
+
- `/cc` — **Cockpit CLI**: drive the cockpit CLI surface — codegraph subcommands, terminal observation, browser automation
|
|
110
|
+
- **Custom**: drop any `SKILL.md` and add it via the Skills sidebar — it auto-appears in the slash autocomplete menu (see [Skills](#skills--extensibility))
|
|
110
111
|
|
|
111
112
|
### Scheduled tasks — Cron for AI
|
|
112
113
|
|
|
@@ -125,7 +126,7 @@ Cockpit is the instrument panel. It does **not** replace Claude Code; it stands
|
|
|
125
126
|
- **Day one in an unfamiliar repo:** Open it in Code Map, click through caller/callee pins, walk the auth flow in five clicks instead of a 90-minute file-tree scavenger hunt.
|
|
126
127
|
- **Two-person team:** Senior reviews via LAN-shared review page, no GitHub PR round-trip needed for in-progress work.
|
|
127
128
|
- **Reviewing AI-generated PRs:** Switch the changed files into Code Map — changed functions are highlighted with their callers / callees still drawn around them, so blast-radius is one click away.
|
|
128
|
-
- **Full-stack chore mode:** `/fx` in one tab on a backend bug, `/
|
|
129
|
+
- **Full-stack chore mode:** `/fx` in one tab on a backend bug, `/ex` in another to plan the frontend refactor, `/go` to land it — three slash modes, three different agent postures.
|
|
129
130
|
- **Cheap second opinion:** Same prompt in two tabs — Claude in one, **DeepSeek v4-pro** in the other — compare answers before you trust either.
|
|
130
131
|
- **AI-driven QA:** Browser Bubble + scheduled task = "every night at 2 AM, run this UI smoke flow and post a summary".
|
|
131
132
|
- **Privacy-sensitive code:** runs on your laptop. Pair with an **Ollama** tab for fully air-gapped sessions. No telemetry, no relay.
|
package/README.zh.md
CHANGED
|
@@ -49,7 +49,7 @@ Cockpit 就是那个仪表盘。它**不替代** Claude Code,而是站在官
|
|
|
49
49
|
| Agent 够不到浏览器 / 数据库 | **智能气泡**:Chrome、PostgreSQL、MySQL、Redis —— Agent 可驱动 |
|
|
50
50
|
| 读陌生仓库就是 90 分钟"找地鼠" | **代码地图(Code Map)** chip 视图 —— caller / callee pin 一点即跳 |
|
|
51
51
|
| AI 输出审阅低效 | **局域网共享评审页**、行级评论、任意评论可回喂给 AI |
|
|
52
|
-
| 每天写一遍"做 X 但不要动代码" | **斜杠模式** `/qa /fx /
|
|
52
|
+
| 每天写一遍"做 X 但不要动代码" | **斜杠模式** `/qa /fx /ex /go /cg /cc` + 通过 Skills 侧边栏自定义 `SKILL.md` |
|
|
53
53
|
| 没有自动化触发器 | 一次性 / 间隔 / **Cron** **定时任务** |
|
|
54
54
|
| 担心"云端中转" | **完全本地**。无遥测,Codex / DeepSeek / Kimi 的 Key 仅存在本机 `~/.cockpit/settings.json` |
|
|
55
55
|
|
|
@@ -80,7 +80,7 @@ Cockpit 就是那个仪表盘。它**不替代** Claude Code,而是站在官
|
|
|
80
80
|
- Git **blame**、Diff 视图、分支切换、**Worktree** 管理
|
|
81
81
|
- **LSP 集成** —— 跳转定义、查找引用、悬浮类型信息
|
|
82
82
|
- **代码地图(Code Map)** —— 每个函数渲染为 chip,左右两侧分别列出 caller / callee,点击 pin 即可顺着调用图走。多语言支持:TS/JS、Python、Go、Rust。无需 LSP、无需项目预热,离线可用。
|
|
83
|
-
- **CodeGraph(项目图谱)** —— 给 Agent 的**代码图谱(code graph)**:同一份 tree-sitter 索引开放为
|
|
83
|
+
- **CodeGraph(项目图谱)** —— 给 Agent 的**代码图谱(code graph)**:同一份 tree-sitter 索引开放为 10 个 HTTP 接口 —— 基础 6 个(`search` / `callers` / `callees` / `impact` / `file` / `coedit`)+ 分析 4 个(`context` / `related` / `risk` / `affected`,由 PageRank · PPR · TF-IDF · Louvain 驱动,零训练),Agent 直接按坐标精确查询,无须 grep 字面。在 chat 输入 `/cg` 触发。只返坐标不返源码——比 grep 精确、比 Read 省 token,还能抓住 regex 看不见的「约定耦合」。
|
|
84
84
|
- 模糊搜索 (Cmd+F)、JSON 查看器、Markdown 预览
|
|
85
85
|
|
|
86
86
|
### Console —— 终端与智能气泡
|
|
@@ -103,10 +103,11 @@ Cockpit 就是那个仪表盘。它**不替代** Claude Code,而是站在官
|
|
|
103
103
|
|
|
104
104
|
- `/qa` —— **只澄清**:复述、反问、绝不写代码
|
|
105
105
|
- `/fx` —— **只诊断**:Bug 证据链分析,绝不改文件
|
|
106
|
-
- `/
|
|
107
|
-
- `/
|
|
108
|
-
- `/cg` —— **CodeGraph 项目探索**:
|
|
109
|
-
-
|
|
106
|
+
- `/ex` —— **探讨**:6 步结构化讨论骨架(研究 → 发散 → 收敛 → 再发散 → 迭代验证 → 总结)
|
|
107
|
+
- `/go` —— **落地**:把收敛后的方案切成 MVP 阶段,每阶段自运行验证,最后端到端回看
|
|
108
|
+
- `/cg` —— **CodeGraph 项目探索**:10 个 HTTP 接口按符号 / 调用关系 / 影响范围 / 协同编辑 / 风险 / 受影响测试精确查询(比 grep 精准)
|
|
109
|
+
- `/cc` —— **Cockpit CLI**:驾驭 cockpit 命令行 —— codegraph 子命令、terminal 观察、browser 自动化
|
|
110
|
+
- **自定义**:任意 `SKILL.md` 通过 Skills 侧边栏添加 —— 自动出现在斜杠补全菜单(见 [Skills](#skills--可扩展性))
|
|
110
111
|
|
|
111
112
|
### 定时任务 —— 给 AI 的 Cron
|
|
112
113
|
|
|
@@ -125,7 +126,7 @@ Cockpit 就是那个仪表盘。它**不替代** Claude Code,而是站在官
|
|
|
125
126
|
- **新仓库的第一天:** 用 Code Map 打开它,沿着 caller / callee pin 一路点过去 —— 鉴权流程 5 次点击就走完,不再是 90 分钟的"文件树找地鼠"。
|
|
126
127
|
- **二人小团队:** Senior 用局域网共享评审页 review,半成品代码不用绕 GitHub PR。
|
|
127
128
|
- **评审 AI 写的 PR:** 把改动文件切到 Code Map,**改动过的函数被高亮**,周围还画着它们的 caller / callee —— 一眼看清这次改动的爆炸半径。
|
|
128
|
-
- **全栈杂活模式:** 后端 bug 一个 tab 跑 `/fx
|
|
129
|
+
- **全栈杂活模式:** 后端 bug 一个 tab 跑 `/fx`,另一个 tab 跑 `/ex` 规划前端重构,最后 `/go` 落地 —— 三种姿态、三种 Agent 模式。
|
|
129
130
|
- **便宜的二次意见:** 同一个 prompt 在两个 tab 跑 —— 一个 Claude、一个 **DeepSeek v4-pro**,对比答案后再决定相信谁。
|
|
130
131
|
- **AI 自动化 QA:** 浏览器气泡 + 定时任务 = "每晚 2 点跑一遍 UI 冒烟流程并发摘要"。
|
|
131
132
|
- **隐私敏感代码:** 在你的笔记本上跑。配合 **Ollama** tab 即可完全断网工作。无遥测、无中转。
|
package/bin/cock-browser.mjs
CHANGED
|
@@ -40,6 +40,32 @@ Usage: cockpit browser ${prefix} <action>`);
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
console.log(`
|
|
43
|
+
── React / SPA gotchas ────────────────────────────────
|
|
44
|
+
On modern SPAs (React, Vue, tiptap, ProseMirror, Lexical, Slate, …)
|
|
45
|
+
\`type\` / \`click\` via CDP often silently no-op because the framework
|
|
46
|
+
ignores raw key/mouse events and reacts only to its own synthetic
|
|
47
|
+
event flow. When typing into a contenteditable / controlled input,
|
|
48
|
+
or clicking a button rendered by a portal, **prefer \`evaluate\`**.
|
|
49
|
+
|
|
50
|
+
Three templates that always work:
|
|
51
|
+
|
|
52
|
+
# 1) Fill a contenteditable (tiptap / ProseMirror / Lexical):
|
|
53
|
+
evaluate "(() => { const el = document.querySelector('[contenteditable=\\"true\\"]'); el.focus(); document.execCommand('insertText', false, 'hello'); return el.innerText; })()"
|
|
54
|
+
|
|
55
|
+
# 2) Set a React-controlled <input> via the native setter + input event:
|
|
56
|
+
evaluate "(() => { const el = document.querySelector('input[name=foo]'); const set = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; set.call(el, 'hello'); el.dispatchEvent(new Event('input', { bubbles: true })); return el.value; })()"
|
|
57
|
+
|
|
58
|
+
# 3) Click a button by aria-label / text (refs go stale after re-render):
|
|
59
|
+
evaluate "(() => { const btn = Array.from(document.querySelectorAll('button')).find(b => b.getAttribute('aria-label') === 'Send message' || b.textContent.trim() === 'Send'); if (!btn) return 'not-found'; btn.click(); return 'clicked'; })()"
|
|
60
|
+
|
|
61
|
+
Refs (e5, e23, …) returned by \`snapshot\` are valid only for the
|
|
62
|
+
DOM at snapshot time. Any re-render / route change / focus shift
|
|
63
|
+
invalidates them — \`Element ref "eN" not found or disconnected\`
|
|
64
|
+
means you need a fresh \`snapshot\` (or just use \`evaluate\` with
|
|
65
|
+
CSS selectors).
|
|
66
|
+
|
|
67
|
+
──────────────────────────────────────────────────────
|
|
68
|
+
|
|
43
69
|
Navigation:
|
|
44
70
|
navigate <url> Navigate to URL
|
|
45
71
|
reload [--noCache] Reload page
|
|
@@ -52,9 +78,9 @@ Inspection:
|
|
|
52
78
|
screenshot Take a screenshot
|
|
53
79
|
|
|
54
80
|
Interaction (use ref from snapshot):
|
|
55
|
-
click <ref> Click element
|
|
56
|
-
type <ref> <text> Type text
|
|
57
|
-
fill <ref> <value> Fill input value
|
|
81
|
+
click <ref> Click element ⚠ React/SPA: may silently miss; use evaluate
|
|
82
|
+
type <ref> <text> Type text ⚠ React/SPA: may silently miss; use evaluate (template 1/2)
|
|
83
|
+
fill <ref> <value> Fill input value ⚠ Same — prefer template 2
|
|
58
84
|
hover <ref> Hover element
|
|
59
85
|
focus <ref> Focus element
|
|
60
86
|
scroll --direction D Scroll page (up/down/left/right)
|
|
@@ -337,6 +363,70 @@ async function run() {
|
|
|
337
363
|
// 这里透明地把完整内容拉回来,调用方看不到 chunking 细节。
|
|
338
364
|
const resolved = await autoResolveChunked(baseUrl, id, data.data, timeout);
|
|
339
365
|
|
|
366
|
+
// Post-`type` verification — `type` via CDP dispatchKeyEvent silently no-ops
|
|
367
|
+
// on React-controlled / contenteditable inputs (tiptap, ProseMirror, Lexical,
|
|
368
|
+
// Slate, etc.) because those frameworks update state from synthetic
|
|
369
|
+
// InputEvents rather than physical key events. The CLI used to return
|
|
370
|
+
// `{typed, ref}` regardless, lying about the result. Now we read back the
|
|
371
|
+
// currently-focused element's value/textContent and compare; mismatches
|
|
372
|
+
// exit 1 with a pointer to the evaluate workaround. Session 0a975cff
|
|
373
|
+
// burned 4 extra bash calls discovering this silently.
|
|
374
|
+
//
|
|
375
|
+
// We use document.activeElement because `type` focuses the target as its
|
|
376
|
+
// first step — so right after a successful type, activeElement IS the
|
|
377
|
+
// target. The Playwright "ref" (e555 etc.) has no DOM-side equivalent, so
|
|
378
|
+
// we can't query for it directly.
|
|
379
|
+
if (action === 'type' && params.ref && typeof params.text === 'string') {
|
|
380
|
+
const expected = String(params.text);
|
|
381
|
+
let actual = '';
|
|
382
|
+
let kind = 'unknown';
|
|
383
|
+
try {
|
|
384
|
+
const verifyJs =
|
|
385
|
+
'(() => { const el = document.activeElement; ' +
|
|
386
|
+
'if (!el || el === document.body) return { kind: "no-active", text: "" }; ' +
|
|
387
|
+
'const tag = el.tagName ? el.tagName.toLowerCase() : ""; ' +
|
|
388
|
+
'if (tag === "input" || tag === "textarea") return { kind: tag, text: el.value || "" }; ' +
|
|
389
|
+
'if (el.isContentEditable) return { kind: "contenteditable", text: el.innerText || el.textContent || "" }; ' +
|
|
390
|
+
'return { kind: tag || "unknown", text: (el.textContent || "").slice(0, 500) }; })()';
|
|
391
|
+
const v = await fetch(`${baseUrl}/api/browser/evaluate`, {
|
|
392
|
+
method: 'POST',
|
|
393
|
+
headers: { 'Content-Type': 'application/json' },
|
|
394
|
+
body: JSON.stringify({ id, params: { js: verifyJs }, timeout: 3000 }),
|
|
395
|
+
signal: AbortSignal.timeout(5000),
|
|
396
|
+
});
|
|
397
|
+
const vj = await v.json();
|
|
398
|
+
const out = vj.ok && vj.data;
|
|
399
|
+
if (out && typeof out === 'object') {
|
|
400
|
+
kind = out.kind;
|
|
401
|
+
actual = String(out.text || '');
|
|
402
|
+
}
|
|
403
|
+
} catch {
|
|
404
|
+
// verification optional — fall through to printing the original result
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Only warn on KNOWN editable types whose value/text doesn't contain what
|
|
408
|
+
// we typed. "no-active" / "unknown" we can't reliably verify, so we skip
|
|
409
|
+
// (no false positives).
|
|
410
|
+
const verifiable = kind === 'input' || kind === 'textarea' || kind === 'contenteditable';
|
|
411
|
+
if (verifiable && !actual.includes(expected)) {
|
|
412
|
+
// Type "succeeded" per CDP but the target didn't pick up the text.
|
|
413
|
+
// Almost always a React-controlled / contenteditable input.
|
|
414
|
+
process.stderr.write(
|
|
415
|
+
`\n⚠️ type succeeded per CDP but the target's value/textContent does NOT contain "${expected}".\n` +
|
|
416
|
+
` target kind: ${kind}, current text: ${JSON.stringify(actual).slice(0, 200)}\n` +
|
|
417
|
+
` This is usually a React-controlled or contenteditable input (tiptap / ProseMirror / Lexical / Slate)\n` +
|
|
418
|
+
` where CDP key events don't trigger framework state updates.\n` +
|
|
419
|
+
`\n` +
|
|
420
|
+
` Fix: use \`evaluate\` to drive the framework's own event flow, e.g.\n` +
|
|
421
|
+
` cockpit browser ${id} evaluate "(() => { const el = document.querySelector('[contenteditable=\\\"true\\\"]'); el.focus(); document.execCommand('insertText', false, ${JSON.stringify(expected)}); return el.innerText; })()"\n` +
|
|
422
|
+
`\n For a plain <input>, set value via property setter + dispatch an 'input' event:\n` +
|
|
423
|
+
` evaluate "(() => { const el = document.querySelector('input[name=foo]'); const set = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; set.call(el, ${JSON.stringify(expected)}); el.dispatchEvent(new Event('input', { bubbles: true })); return el.value; })()"\n`
|
|
424
|
+
);
|
|
425
|
+
await formatOutput(action, resolved);
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
340
430
|
// Format output
|
|
341
431
|
await formatOutput(action, resolved);
|
|
342
432
|
} catch (err) {
|