wezard 0.3.3
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/.claude-plugin/marketplace.json +14 -0
- package/.claude-plugin/plugin.json +14 -0
- package/CLAUDE.md +83 -0
- package/LICENSE +21 -0
- package/README.md +410 -0
- package/cli/weclaude.sh +236 -0
- package/commands/audit.md +7 -0
- package/commands/wrc.md +7 -0
- package/config.example.jsonc +102 -0
- package/dist/cli/audit.js +156 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/init.js +311 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/sync.js +175 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/daemon/approval.js +1301 -0
- package/dist/daemon/approval.js.map +1 -0
- package/dist/daemon/ask.js +101 -0
- package/dist/daemon/ask.js.map +1 -0
- package/dist/daemon/audit.js +168 -0
- package/dist/daemon/audit.js.map +1 -0
- package/dist/daemon/cc-bridge.js +204 -0
- package/dist/daemon/cc-bridge.js.map +1 -0
- package/dist/daemon/cfg-sync.js +240 -0
- package/dist/daemon/cfg-sync.js.map +1 -0
- package/dist/daemon/claim.js +95 -0
- package/dist/daemon/claim.js.map +1 -0
- package/dist/daemon/detail.js +153 -0
- package/dist/daemon/detail.js.map +1 -0
- package/dist/daemon/graph.js +176 -0
- package/dist/daemon/graph.js.map +1 -0
- package/dist/daemon/http.js +83 -0
- package/dist/daemon/http.js.map +1 -0
- package/dist/daemon/inbound.js +919 -0
- package/dist/daemon/inbound.js.map +1 -0
- package/dist/daemon/index.js +569 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/last-response.js +59 -0
- package/dist/daemon/last-response.js.map +1 -0
- package/dist/daemon/mirror-bridge.js +3512 -0
- package/dist/daemon/mirror-bridge.js.map +1 -0
- package/dist/daemon/mirror-store.js +39 -0
- package/dist/daemon/mirror-store.js.map +1 -0
- package/dist/daemon/outbound.js +42 -0
- package/dist/daemon/outbound.js.map +1 -0
- package/dist/daemon/peers.js +146 -0
- package/dist/daemon/peers.js.map +1 -0
- package/dist/daemon/pending.js +90 -0
- package/dist/daemon/pending.js.map +1 -0
- package/dist/daemon/quota.js +164 -0
- package/dist/daemon/quota.js.map +1 -0
- package/dist/daemon/redact.js +27 -0
- package/dist/daemon/redact.js.map +1 -0
- package/dist/daemon/session-cache.js +149 -0
- package/dist/daemon/session-cache.js.map +1 -0
- package/dist/daemon/session-scan.js +273 -0
- package/dist/daemon/session-scan.js.map +1 -0
- package/dist/daemon/sessions.js +35 -0
- package/dist/daemon/sessions.js.map +1 -0
- package/dist/daemon/spawn-tmux.js +213 -0
- package/dist/daemon/spawn-tmux.js.map +1 -0
- package/dist/daemon/tips.js +25 -0
- package/dist/daemon/tips.js.map +1 -0
- package/dist/daemon/topics.js +197 -0
- package/dist/daemon/topics.js.map +1 -0
- package/dist/daemon/usage.js +358 -0
- package/dist/daemon/usage.js.map +1 -0
- package/dist/daemon/wedoc.js +210 -0
- package/dist/daemon/wedoc.js.map +1 -0
- package/dist/daemon/ws.js +67 -0
- package/dist/daemon/ws.js.map +1 -0
- package/dist/mcp/server.js +385 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/shared/ansi.js +134 -0
- package/dist/shared/ansi.js.map +1 -0
- package/dist/shared/chat-http.js +195 -0
- package/dist/shared/chat-http.js.map +1 -0
- package/dist/shared/chat-render.js +53 -0
- package/dist/shared/chat-render.js.map +1 -0
- package/dist/shared/chat-view.js +116 -0
- package/dist/shared/chat-view.js.map +1 -0
- package/dist/shared/cli-backends.js +304 -0
- package/dist/shared/cli-backends.js.map +1 -0
- package/dist/shared/config-writer.js +39 -0
- package/dist/shared/config-writer.js.map +1 -0
- package/dist/shared/config.js +278 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/shared/detail-render.js +836 -0
- package/dist/shared/detail-render.js.map +1 -0
- package/dist/shared/detail-store.js +205 -0
- package/dist/shared/detail-store.js.map +1 -0
- package/dist/shared/highlight.js +154 -0
- package/dist/shared/highlight.js.map +1 -0
- package/dist/shared/lan-ip.js +35 -0
- package/dist/shared/lan-ip.js.map +1 -0
- package/dist/shared/log.js +18 -0
- package/dist/shared/log.js.map +1 -0
- package/dist/shared/md-chunk.js +99 -0
- package/dist/shared/md-chunk.js.map +1 -0
- package/dist/shared/paths.js +12 -0
- package/dist/shared/paths.js.map +1 -0
- package/dist/shared/session-label.js +67 -0
- package/dist/shared/session-label.js.map +1 -0
- package/dist/shared/web-assets.js +51 -0
- package/dist/shared/web-assets.js.map +1 -0
- package/dist/svr/index.js +246 -0
- package/dist/svr/index.js.map +1 -0
- package/hooks/hooks.json +16 -0
- package/hooks/pre-tool-use.sh +167 -0
- package/launchd/com.weclaude.daemon.plist.template +45 -0
- package/package.json +97 -0
- package/scripts/install.sh +73 -0
- package/scripts/postinstall.sh +12 -0
- package/scripts/uninstall.sh +44 -0
- package/systemd/weclaude.service.template +17 -0
- package/web/chat.css +171 -0
- package/web/chat.js +367 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// Scan the host for live `claude` sessions running inside tmux, and summarize
|
|
2
|
+
// what each is doing from its transcript tail. Used by the /sessions/* routes
|
|
3
|
+
// so a WeCom-side Claude can list / switch / describe sibling sessions.
|
|
4
|
+
//
|
|
5
|
+
// Why daemon-side: an MCP tool runs *inside one* claude process and can only
|
|
6
|
+
// see its own $TMUX_PANE / cwd / sessionId. Correlating *other* sessions to
|
|
7
|
+
// their tmux pane (required for IM→CLI inject) needs a host-wide /proc + tmux
|
|
8
|
+
// sweep, which only the resident daemon is positioned to do.
|
|
9
|
+
//
|
|
10
|
+
// Scope: tmux-hosted sessions only (a session with no pane can't receive
|
|
11
|
+
// pasted input, so it's not a useful mirror target). Linux-only (reads /proc);
|
|
12
|
+
// returns [] elsewhere, degrading gracefully.
|
|
13
|
+
import { spawn } from "node:child_process";
|
|
14
|
+
import { readdirSync, readFileSync, existsSync, statSync, readlinkSync } from "node:fs";
|
|
15
|
+
import { join, basename } from "node:path";
|
|
16
|
+
import { activeBackends, backendForPath, projectDirsFor } from "../shared/cli-backends.js";
|
|
17
|
+
import { expandHome } from "../shared/paths.js";
|
|
18
|
+
import { labelFor } from "../shared/session-label.js";
|
|
19
|
+
import { summarizeTail } from "./peers.js";
|
|
20
|
+
const dirnameOfNode = () => {
|
|
21
|
+
const i = process.execPath.lastIndexOf("/");
|
|
22
|
+
return i > 0 ? process.execPath.slice(0, i) : "";
|
|
23
|
+
};
|
|
24
|
+
const augmentedPath = (orig) => {
|
|
25
|
+
const extras = [dirnameOfNode(), "/opt/homebrew/bin", "/usr/local/bin", `${process.env.HOME ?? ""}/.local/bin`].filter(Boolean);
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
return [orig ?? "", ...extras]
|
|
28
|
+
.flatMap((p) => p.split(":"))
|
|
29
|
+
.filter((p) => p && !seen.has(p) && (seen.add(p), true))
|
|
30
|
+
.join(":");
|
|
31
|
+
};
|
|
32
|
+
const runTmux = (args) => new Promise((resolve) => {
|
|
33
|
+
const p = spawn("tmux", args, {
|
|
34
|
+
env: { ...process.env, PATH: augmentedPath(process.env.PATH) },
|
|
35
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
36
|
+
});
|
|
37
|
+
let out = "";
|
|
38
|
+
p.stdout?.on("data", (c) => (out += c.toString("utf8")));
|
|
39
|
+
p.on("error", () => resolve({ ok: false, stdout: "" }));
|
|
40
|
+
p.on("close", (code) => resolve({ ok: code === 0, stdout: out }));
|
|
41
|
+
});
|
|
42
|
+
// Read /proc/<pid>/stat → ppid. comm (2nd field) is wrapped in parens and may
|
|
43
|
+
// itself contain spaces/parens, so split on the LAST ')' and index from there:
|
|
44
|
+
// fields after comm are state ppid ... → ppid is index 1 of the tail.
|
|
45
|
+
const ppidOf = (pid) => {
|
|
46
|
+
try {
|
|
47
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
48
|
+
const rp = stat.lastIndexOf(")");
|
|
49
|
+
if (rp < 0)
|
|
50
|
+
return null;
|
|
51
|
+
const rest = stat.slice(rp + 2).split(" ");
|
|
52
|
+
const ppid = parseInt(rest[1] ?? "", 10);
|
|
53
|
+
return Number.isInteger(ppid) ? ppid : null;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const cmdlineOf = (pid) => {
|
|
60
|
+
try {
|
|
61
|
+
return readFileSync(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ").trim();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return "";
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const cwdOf = (pid) => {
|
|
68
|
+
try {
|
|
69
|
+
return readlinkSync(`/proc/${pid}/cwd`);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return "";
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
// Pull a `--session-id <uuid>` out of a cmdline. Returns "" when absent
|
|
76
|
+
// (e.g. claude launched with bare `-r`/`--resume` — no explicit id to bind).
|
|
77
|
+
const sessionIdFromCmd = (cmd) => {
|
|
78
|
+
const m = cmd.match(/--session-id[= ]+([0-9a-fA-F-]{36})/);
|
|
79
|
+
return m?.[1] ?? "";
|
|
80
|
+
};
|
|
81
|
+
// A process belongs to an agent CLI if its cmdline mentions any active
|
|
82
|
+
// backend's binary name. Derived from the registry rather than hardcoded, so a
|
|
83
|
+
// custom `cliBackends.<name>.bin` is recognized too — without this, codebuddy
|
|
84
|
+
// panes were invisible to the scan and could never be listed or switched to.
|
|
85
|
+
const looksLikeAgentCli = (cmd) => activeBackends().some((b) => cmd.toLowerCase().includes(basename(b.bin).toLowerCase()));
|
|
86
|
+
// Transcript roots of every active backend, in registry order (primary first).
|
|
87
|
+
const projectRoots = () => activeBackends().map((b) => expandHome(b.projectsDir));
|
|
88
|
+
// Locate `<sessionId>.jsonl` (UUID is globally unique, so we don't have to
|
|
89
|
+
// reverse the cwd→dir encoding). Skips subagent transcripts (they live under a
|
|
90
|
+
// `<sid>/subagents/` subdir, never directly under a project dir).
|
|
91
|
+
const findJsonl = (sessionId) => {
|
|
92
|
+
const name = `${sessionId}.jsonl`;
|
|
93
|
+
for (const root of projectRoots()) {
|
|
94
|
+
if (!existsSync(root))
|
|
95
|
+
continue;
|
|
96
|
+
let subs;
|
|
97
|
+
try {
|
|
98
|
+
subs = readdirSync(root, { withFileTypes: true });
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
for (const d of subs) {
|
|
104
|
+
if (!d.isDirectory())
|
|
105
|
+
continue;
|
|
106
|
+
const p = join(root, d.name, name);
|
|
107
|
+
if (existsSync(p))
|
|
108
|
+
return p;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return "";
|
|
112
|
+
};
|
|
113
|
+
// cwd→projectDir encoding is backend-specific; projectDirsFor yields one
|
|
114
|
+
// candidate per active backend (primary first) using each one's own dialect.
|
|
115
|
+
// (The old local copies diverged — the Claude encoder here also mapped `_`,
|
|
116
|
+
// which does NOT match what Claude Code writes.)
|
|
117
|
+
// Expected jsonl path for a freshly-spawned session whose transcript doesn't
|
|
118
|
+
// exist on disk yet (a CLI only writes it after the first user input). Used so
|
|
119
|
+
// a brand-new `new_claude_session` still shows up in the list immediately.
|
|
120
|
+
const expectedJsonl = (cwd, sessionId) => {
|
|
121
|
+
if (!cwd || !sessionId)
|
|
122
|
+
return "";
|
|
123
|
+
const cands = projectDirsFor(cwd);
|
|
124
|
+
const hit = cands.find(({ dir }) => existsSync(dir)) ?? cands[0];
|
|
125
|
+
return hit ? join(hit.dir, `${sessionId}.jsonl`) : "";
|
|
126
|
+
};
|
|
127
|
+
// Resume-mode fallback: a claude launched with bare `-r`/`--resume` carries no
|
|
128
|
+
// `--session-id` on its cmdline. Infer the session from its cwd: the most
|
|
129
|
+
// recently written `<uuid>.jsonl` directly under that project dir. `taken`
|
|
130
|
+
// holds session ids already claimed by explicit-id processes so two sessions
|
|
131
|
+
// sharing a cwd don't both resolve to the same newest file.
|
|
132
|
+
const inferByCwd = (cwd, taken) => {
|
|
133
|
+
if (!cwd)
|
|
134
|
+
return null;
|
|
135
|
+
for (const { dir } of projectDirsFor(cwd)) {
|
|
136
|
+
if (!existsSync(dir))
|
|
137
|
+
continue;
|
|
138
|
+
let files;
|
|
139
|
+
try {
|
|
140
|
+
files = readdirSync(dir).filter((n) => n.endsWith(".jsonl"));
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const cands = files
|
|
146
|
+
.map((n) => ({ sid: n.slice(0, -6), p: join(dir, n) }))
|
|
147
|
+
.filter((c) => !taken.has(c.sid))
|
|
148
|
+
.map((c) => ({
|
|
149
|
+
...c,
|
|
150
|
+
m: (() => {
|
|
151
|
+
try {
|
|
152
|
+
return statSync(c.p).mtimeMs;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
})(),
|
|
158
|
+
}))
|
|
159
|
+
.sort((a, b) => b.m - a.m);
|
|
160
|
+
const top = cands[0];
|
|
161
|
+
if (top)
|
|
162
|
+
return { sessionId: top.sid, jsonlPath: top.p };
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
};
|
|
166
|
+
// Build pane_pid → {paneId, session}. We map the *pane's* root pid; the claude
|
|
167
|
+
// worker is a descendant, so we walk its ppid chain up to find a matching pane.
|
|
168
|
+
const tmuxPaneIndex = async () => {
|
|
169
|
+
const r = await runTmux(["list-panes", "-a", "-F", "#{pane_pid}|#{pane_id}|#{session_name}"]);
|
|
170
|
+
const idx = new Map();
|
|
171
|
+
if (!r.ok)
|
|
172
|
+
return idx;
|
|
173
|
+
for (const line of r.stdout.split("\n")) {
|
|
174
|
+
const [panePid, paneId, session] = line.split("|");
|
|
175
|
+
const pid = parseInt(panePid ?? "", 10);
|
|
176
|
+
if (Number.isInteger(pid) && paneId)
|
|
177
|
+
idx.set(pid, { paneId, session: session ?? "" });
|
|
178
|
+
}
|
|
179
|
+
return idx;
|
|
180
|
+
};
|
|
181
|
+
// Climb the ppid chain from `pid` until we hit a pid that owns a tmux pane.
|
|
182
|
+
// Bounded to avoid pathological loops.
|
|
183
|
+
const tmuxOwnerOf = (pid, paneIdx) => {
|
|
184
|
+
let cur = pid;
|
|
185
|
+
for (let i = 0; i < 40 && cur != null && cur > 1; i++) {
|
|
186
|
+
const hit = paneIdx.get(cur);
|
|
187
|
+
if (hit)
|
|
188
|
+
return hit;
|
|
189
|
+
const pp = ppidOf(cur);
|
|
190
|
+
if (pp == null || pp === cur)
|
|
191
|
+
break;
|
|
192
|
+
cur = pp;
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
};
|
|
196
|
+
export const scanClaudeSessions = async () => {
|
|
197
|
+
const paneIdx = await tmuxPaneIndex();
|
|
198
|
+
let pids;
|
|
199
|
+
try {
|
|
200
|
+
pids = readdirSync("/proc").filter((n) => /^\d+$/.test(n)).map((n) => parseInt(n, 10));
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return []; // not Linux / no /proc
|
|
204
|
+
}
|
|
205
|
+
// Collect tmux-hosted claude workers, then collapse to ONE session per pane
|
|
206
|
+
// (a tmux pane runs exactly one claude session; a pane may host several
|
|
207
|
+
// claude processes — shim + worker + nested — which must not each count as
|
|
208
|
+
// a separate session). Per pane: prefer a process carrying an explicit
|
|
209
|
+
// `--session-id`; else fall back to one resume-mode inference by cwd.
|
|
210
|
+
const byPane = new Map();
|
|
211
|
+
for (const pid of pids) {
|
|
212
|
+
const cmd = cmdlineOf(pid);
|
|
213
|
+
if (!cmd || !looksLikeAgentCli(cmd))
|
|
214
|
+
continue;
|
|
215
|
+
const owner = tmuxOwnerOf(pid, paneIdx);
|
|
216
|
+
if (!owner)
|
|
217
|
+
continue; // tmux scope
|
|
218
|
+
const explicitSid = sessionIdFromCmd(cmd);
|
|
219
|
+
const prev = byPane.get(owner.paneId);
|
|
220
|
+
if (!prev || (!prev.explicitSid && explicitSid)) {
|
|
221
|
+
byPane.set(owner.paneId, { pid, owner, cwd: cwdOf(pid), explicitSid });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const seen = new Set();
|
|
225
|
+
const out = [];
|
|
226
|
+
const emit = (sessionId, jsonlPath, cwd, owner) => {
|
|
227
|
+
if (!sessionId || !jsonlPath || seen.has(sessionId))
|
|
228
|
+
return;
|
|
229
|
+
seen.add(sessionId);
|
|
230
|
+
let lastActivity = 0;
|
|
231
|
+
try {
|
|
232
|
+
lastActivity = statSync(jsonlPath).mtimeMs;
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
/* fresh session: no transcript yet */
|
|
236
|
+
}
|
|
237
|
+
out.push({
|
|
238
|
+
sessionId,
|
|
239
|
+
label: labelFor(sessionId),
|
|
240
|
+
cwd,
|
|
241
|
+
jsonlPath,
|
|
242
|
+
tmuxSession: owner.session,
|
|
243
|
+
tmuxPane: owner.paneId,
|
|
244
|
+
lastActivity,
|
|
245
|
+
summary: summarizeTail(jsonlPath),
|
|
246
|
+
cli: backendForPath(jsonlPath).name,
|
|
247
|
+
});
|
|
248
|
+
};
|
|
249
|
+
const panes = Array.from(byPane.values());
|
|
250
|
+
// Pass 1: explicit `--session-id` — exact, no ambiguity. Seeds `seen` so
|
|
251
|
+
// resume inference in pass 2 can't re-claim these ids. A freshly-spawned
|
|
252
|
+
// session may not have written its jsonl yet — fall back to the expected
|
|
253
|
+
// path so it still appears (summary reads as a new/empty session).
|
|
254
|
+
for (const w of panes) {
|
|
255
|
+
if (!w.explicitSid)
|
|
256
|
+
continue;
|
|
257
|
+
const jsonlPath = findJsonl(w.explicitSid) || expectedJsonl(w.cwd, w.explicitSid);
|
|
258
|
+
if (jsonlPath)
|
|
259
|
+
emit(w.explicitSid, jsonlPath, w.cwd, w.owner);
|
|
260
|
+
}
|
|
261
|
+
// Pass 2: resume-mode (`-r`/`--resume`, no id) — infer via cwd→newest jsonl,
|
|
262
|
+
// skipping any session already claimed in pass 1.
|
|
263
|
+
for (const w of panes) {
|
|
264
|
+
if (w.explicitSid)
|
|
265
|
+
continue;
|
|
266
|
+
const inf = inferByCwd(w.cwd, seen);
|
|
267
|
+
if (inf)
|
|
268
|
+
emit(inf.sessionId, inf.jsonlPath, w.cwd, w.owner);
|
|
269
|
+
}
|
|
270
|
+
out.sort((a, b) => b.lastActivity - a.lastActivity);
|
|
271
|
+
return out;
|
|
272
|
+
};
|
|
273
|
+
//# sourceMappingURL=session-scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-scan.js","sourceRoot":"","sources":["../../daemon/session-scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Persisted principal → claude session_id map. Single JSON file, write-through.
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { expandHome } from "../shared/paths.js";
|
|
5
|
+
export const loadSessionStore = (filePath) => {
|
|
6
|
+
const abs = expandHome(filePath);
|
|
7
|
+
let map = {};
|
|
8
|
+
if (existsSync(abs)) {
|
|
9
|
+
try {
|
|
10
|
+
map = JSON.parse(readFileSync(abs, "utf8"));
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
map = {};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
const persist = () => {
|
|
20
|
+
writeFileSync(abs, JSON.stringify(map, null, 2), "utf8");
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
get: (p) => map[p],
|
|
24
|
+
set: (p, sid) => {
|
|
25
|
+
map[p] = sid;
|
|
26
|
+
persist();
|
|
27
|
+
},
|
|
28
|
+
drop: (p) => {
|
|
29
|
+
delete map[p];
|
|
30
|
+
persist();
|
|
31
|
+
},
|
|
32
|
+
all: () => ({ ...map }),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../daemon/sessions.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAShD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,QAAgB,EAAgB,EAAE;IACjE,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACjC,IAAI,GAAG,GAA2B,EAAE,CAAC;IACrC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAA2B,CAAC;QACxE,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,GAAG,EAAE,CAAC;QACX,CAAC;IACH,CAAC;SAAM,CAAC;QACN,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC3D,CAAC,CAAC;IAEF,OAAO;QACL,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAClB,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;YACd,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;YACb,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;YACV,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;YACd,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC;KACxB,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// Auto-spawn helper for mirror mode.
|
|
2
|
+
//
|
|
3
|
+
// Goal: from a WeCom inbound, materialize a fresh `claude` process inside a
|
|
4
|
+
// shared tmux session and attach the resulting jsonl to the WeCom chat —
|
|
5
|
+
// no human-in-the-loop `/wrc` needed.
|
|
6
|
+
//
|
|
7
|
+
// Layout: ONE shared tmux session named `cfg.wrc.tmuxPrefix` (default
|
|
8
|
+
// `weclaude`); each chat gets its own window inside it. The pane id (`%N`)
|
|
9
|
+
// uniquely identifies a chat's claude process — pane ids are monotonic per
|
|
10
|
+
// tmux server lifetime, so they're safe to persist and re-validate via
|
|
11
|
+
// `display-message -t %N`. Window names are cosmetic (sanitized principal id;
|
|
12
|
+
// WeCom doesn't expose a display name).
|
|
13
|
+
//
|
|
14
|
+
// Pipeline:
|
|
15
|
+
// uuid = randomUUID (deterministic session)
|
|
16
|
+
// tmux has-session -t weclaude → create or reuse
|
|
17
|
+
// tmux new-window/new-session -P -F '#{pane_id}' … (→ paneId)
|
|
18
|
+
// tmux send-keys -t %paneId "claudeBin --session-id <uuid> …" Enter
|
|
19
|
+
//
|
|
20
|
+
// We do NOT wait for claude to write anything to the jsonl. The jsonl is the
|
|
21
|
+
// transcript file, populated as claude runs; the empty file is enough for
|
|
22
|
+
// MirrorBridge.attach() (existsSync) and startMirrorTail (statSync.size=0 →
|
|
23
|
+
// tail from offset 0). The brief post-launch settle exists only to give the
|
|
24
|
+
// pane's shell time to finish sourcing rc + claude TUI time to grab the pty
|
|
25
|
+
// before the first paste-buffer inject hits.
|
|
26
|
+
import { spawn } from "node:child_process";
|
|
27
|
+
import { randomUUID } from "node:crypto";
|
|
28
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
29
|
+
import { homedir } from "node:os";
|
|
30
|
+
import { basename, dirname, join } from "node:path";
|
|
31
|
+
import { expandHome } from "../shared/paths.js";
|
|
32
|
+
import { activateBackend, CLI_BACKEND_DEFAULTS, primaryBackend } from "../shared/cli-backends.js";
|
|
33
|
+
const NODE_BIN_DIR = dirname(process.execPath);
|
|
34
|
+
const augmentedPath = (orig) => {
|
|
35
|
+
const extras = [NODE_BIN_DIR, "/opt/homebrew/bin", "/usr/local/bin", `${process.env.HOME ?? ""}/.local/bin`].filter(Boolean);
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
return [orig ?? "", ...extras]
|
|
38
|
+
.flatMap((p) => p.split(":"))
|
|
39
|
+
.filter((p) => p && !seen.has(p) && (seen.add(p), true))
|
|
40
|
+
.join(":");
|
|
41
|
+
};
|
|
42
|
+
// A spawn has no transcript to derive a backend from, so the caller picks one
|
|
43
|
+
// (`cli`) and we fall back to the primary (`wrc.defaultCli`). Everything the
|
|
44
|
+
// pane needs — binary, transcript root, project-dir encoding, trust-marker file
|
|
45
|
+
// — comes from that single descriptor, which is what lets one daemon host
|
|
46
|
+
// claude and codebuddy panes at the same time.
|
|
47
|
+
const backendFor = (cfg, cli) => {
|
|
48
|
+
const primary = primaryBackend();
|
|
49
|
+
if (!cli || cli === primary.name)
|
|
50
|
+
return primary;
|
|
51
|
+
const base = CLI_BACKEND_DEFAULTS[cli];
|
|
52
|
+
const override = cfg.wrc.cliBackends?.[cli]?.bin;
|
|
53
|
+
// Activate before spawning: this CLI's transcript root may not exist yet, in
|
|
54
|
+
// which case the boot-time probe skipped it and backendForPath would misread
|
|
55
|
+
// the jsonl we are about to create as the primary's dialect.
|
|
56
|
+
return activateBackend(override ? { ...base, bin: override } : base);
|
|
57
|
+
};
|
|
58
|
+
export const runTmux = (args) => new Promise((resolve) => {
|
|
59
|
+
const proc = spawn("tmux", args, {
|
|
60
|
+
env: { ...process.env, PATH: augmentedPath(process.env.PATH) },
|
|
61
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
62
|
+
});
|
|
63
|
+
let out = "";
|
|
64
|
+
let err = "";
|
|
65
|
+
proc.stdout?.on("data", (c) => (out += c.toString("utf8")));
|
|
66
|
+
proc.stderr?.on("data", (c) => (err += c.toString("utf8")));
|
|
67
|
+
proc.on("error", (e) => resolve({ ok: false, stdout: "", stderr: e.message, code: null }));
|
|
68
|
+
proc.on("close", (code) => resolve({ ok: code === 0, stdout: out, stderr: err, code }));
|
|
69
|
+
});
|
|
70
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
71
|
+
// Single-quote shell-escape: wrap in '…' and escape embedded ' as '\''.
|
|
72
|
+
const shQuote = (a) => (/[\s"'`$\\]/.test(a) ? `'${a.replace(/'/g, "'\\''")}'` : a);
|
|
73
|
+
// tmux window names are free-form but a status-bar friendly slug avoids
|
|
74
|
+
// surprises (no whitespace / colons / quoting hazards). Principals are
|
|
75
|
+
// shortened: `user:foo` → `u-foo`, `chat:foo` → `c-foo`, and get truncated to
|
|
76
|
+
// 8 chars total (status bar real estate is scarce; collisions don't matter —
|
|
77
|
+
// we always address windows by pane id, never by name). Explicit tag names
|
|
78
|
+
// (not `user:`/`chat:` prefixed) are treated as user-authored labels and kept
|
|
79
|
+
// up to 24 chars so `/new #my-tag` shows readably in the tmux status bar.
|
|
80
|
+
const safeWindowName = (s) => {
|
|
81
|
+
const isPrincipal = s.startsWith("user:") || s.startsWith("chat:");
|
|
82
|
+
const compact = s.startsWith("user:") ? `u-${s.slice(5)}`
|
|
83
|
+
: s.startsWith("chat:") ? `c-${s.slice(5)}`
|
|
84
|
+
: s;
|
|
85
|
+
const slug = compact.replace(/[^A-Za-z0-9_.\-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
86
|
+
return (slug || "claude").slice(0, isPrincipal ? 8 : 24);
|
|
87
|
+
};
|
|
88
|
+
// Time the daemon waits between launching claude and returning. Covers shell
|
|
89
|
+
// rc sourcing + claude TUI grabbing the pty + reaching interactive state so
|
|
90
|
+
// the very next paste-buffer inject lands inside claude's input box AND the
|
|
91
|
+
// trailing Enter is honored as submit (not eaten by the startup banner).
|
|
92
|
+
// 1.5s used to leave Enter racing with TUI init — text would land but the
|
|
93
|
+
// submit got dropped, leaving the prompt typed-but-unsent.
|
|
94
|
+
const TUI_SETTLE_MS = 3000;
|
|
95
|
+
const claudeConfigCandidates = (claudeBin) => {
|
|
96
|
+
const home = homedir();
|
|
97
|
+
const name = basename(claudeBin);
|
|
98
|
+
if (name === "claude")
|
|
99
|
+
return [join(home, ".claude.json")];
|
|
100
|
+
return [join(home, `.${name}`, ".claude.json"), join(home, `.${name}.json`)];
|
|
101
|
+
};
|
|
102
|
+
export const trustWorkspace = (claudeBin, cwd, log) => {
|
|
103
|
+
const candidates = claudeConfigCandidates(claudeBin);
|
|
104
|
+
const target = candidates.find(existsSync) ?? candidates[0];
|
|
105
|
+
let cfg = {};
|
|
106
|
+
try {
|
|
107
|
+
if (existsSync(target))
|
|
108
|
+
cfg = JSON.parse(readFileSync(target, "utf8"));
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
log.warn({ target, err: e.message }, "trustWorkspace: parse failed; rewriting");
|
|
112
|
+
}
|
|
113
|
+
const projects = (cfg.projects ??= {});
|
|
114
|
+
const proj = projects[cwd] ?? {};
|
|
115
|
+
if (proj.hasTrustDialogAccepted && (proj.projectOnboardingSeenCount ?? 0) >= 1 && proj.hasClaudeMdExternalIncludesApproved) {
|
|
116
|
+
return; // already trusted — no write needed
|
|
117
|
+
}
|
|
118
|
+
projects[cwd] = {
|
|
119
|
+
...proj,
|
|
120
|
+
hasTrustDialogAccepted: true,
|
|
121
|
+
projectOnboardingSeenCount: Math.max(proj.projectOnboardingSeenCount ?? 0, 1),
|
|
122
|
+
hasClaudeMdExternalIncludesApproved: true,
|
|
123
|
+
hasClaudeMdExternalIncludesWarningShown: true,
|
|
124
|
+
};
|
|
125
|
+
try {
|
|
126
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
127
|
+
writeFileSync(target, JSON.stringify(cfg, null, 2));
|
|
128
|
+
log.info({ target, cwd }, "trustWorkspace: marker written");
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
log.warn({ target, err: e.message }, "trustWorkspace: write failed");
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
export const spawnTmuxClaude = async ({ cfg, log, resumeSessionId, windowName, cwdOverride, cli, model }) => {
|
|
135
|
+
const backend = backendFor(cfg, cli);
|
|
136
|
+
const cwd = expandHome((cwdOverride ?? "").trim() || cfg.wrc.cwd);
|
|
137
|
+
const projectDir = join(expandHome(backend.projectsDir), backend.encodeProjectDir(cwd));
|
|
138
|
+
const sessionId = resumeSessionId ?? randomUUID();
|
|
139
|
+
const jsonlPath = join(projectDir, `${sessionId}.jsonl`);
|
|
140
|
+
const tmuxName = cfg.wrc.tmuxPrefix; // shared session for all chats
|
|
141
|
+
const winName = safeWindowName(windowName ?? sessionId);
|
|
142
|
+
// Probe tmux availability up front — clearer error than a generic spawn fail.
|
|
143
|
+
const probe = await runTmux(["-V"]);
|
|
144
|
+
if (!probe.ok)
|
|
145
|
+
return { ok: false, reason: `tmux not available: ${probe.stderr.trim() || probe.code}` };
|
|
146
|
+
// Ensure cwd / projectDir exist (tmux `new-session -c` fails if cwd missing;
|
|
147
|
+
// default `~/.weclaude/workspace` often hasn't been touched yet). Do NOT
|
|
148
|
+
// pre-create the jsonl: `claude --session-id <uuid>` refuses to start when
|
|
149
|
+
// the transcript file already exists ("session id is already in use"). We
|
|
150
|
+
// poll for it after spawn instead.
|
|
151
|
+
try {
|
|
152
|
+
mkdirSync(cwd, { recursive: true });
|
|
153
|
+
mkdirSync(projectDir, { recursive: true });
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
return { ok: false, reason: `prepare cwd failed: ${e.message}` };
|
|
157
|
+
}
|
|
158
|
+
// Pre-trust the workspace so claude TUI doesn't park on the "Do you trust
|
|
159
|
+
// this folder?" / onboarding prompts — those prompts swallow paste-buffer
|
|
160
|
+
// injects and the approval card never fires. Best-effort: log + continue
|
|
161
|
+
// on failure (worst case the user clicks through the prompts manually).
|
|
162
|
+
trustWorkspace(backend.bin, cwd, log);
|
|
163
|
+
// Reuse the shared session if alive; otherwise create it. Either way, end
|
|
164
|
+
// up with a fresh window whose pane id we capture via `-P -F '#{pane_id}'`.
|
|
165
|
+
//
|
|
166
|
+
// Seed the new pane's environment (`-e`, tmux ≥3.0) BEFORE its shell sources
|
|
167
|
+
// rc: oh-my-zsh runs its update check while sourcing .zshrc and, when due,
|
|
168
|
+
// blocks on `[oh-my-zsh] Would you like to update? [Y/n]`. That prompt eats
|
|
169
|
+
// the `claude …` command line + the trailing Enter, so claude never launches
|
|
170
|
+
// and the injected message falls through to a bare shell (`command not
|
|
171
|
+
// found: hi`). These two legacy vars (still honored for back-compat) disable
|
|
172
|
+
// the prompt regardless of the user's zstyle config.
|
|
173
|
+
const paneEnv = ["-e", "DISABLE_AUTO_UPDATE=true", "-e", "DISABLE_UPDATE_PROMPT=true"];
|
|
174
|
+
const has = await runTmux(["has-session", "-t", tmuxName]);
|
|
175
|
+
const created = has.code === 0
|
|
176
|
+
// `${tmuxName}:` (trailing colon) forces session-only resolution → next free
|
|
177
|
+
// window index. Bare `-t weclaude` is ambiguous: if a *window* is also named
|
|
178
|
+
// `weclaude`, tmux matches it and tries to reuse its index → "index N in use".
|
|
179
|
+
? await runTmux(["new-window", "-d", "-t", `${tmuxName}:`, "-n", winName, "-c", cwd, ...paneEnv, "-P", "-F", "#{pane_id}"])
|
|
180
|
+
: await runTmux(["new-session", "-d", "-s", tmuxName, "-n", winName, "-c", cwd, ...paneEnv, "-P", "-F", "#{pane_id}"]);
|
|
181
|
+
if (!created.ok) {
|
|
182
|
+
const verb = has.code === 0 ? "new-window" : "new-session";
|
|
183
|
+
return { ok: false, reason: `tmux ${verb} failed: ${created.stderr.trim() || created.code}` };
|
|
184
|
+
}
|
|
185
|
+
const tmuxPane = created.stdout.split("\n").map((s) => s.trim()).filter(Boolean)[0] ?? "";
|
|
186
|
+
if (!tmuxPane) {
|
|
187
|
+
return { ok: false, reason: "tmux returned no pane id" };
|
|
188
|
+
}
|
|
189
|
+
log.info({ tmuxName, winName, tmuxPane, cwd, sessionId, cli: backend.name, reused: has.code === 0 }, "spawn-tmux: pane created");
|
|
190
|
+
// DISABLE_AUTOUPDATER=1 防止新 pane 启动时弹出 "An update is available" 询问 ——
|
|
191
|
+
// 该交互会吞掉首条 paste-buffer 注入,导致仅落字符不进入 claude 输入框。
|
|
192
|
+
const cmd = [
|
|
193
|
+
"DISABLE_AUTOUPDATER=1",
|
|
194
|
+
backend.bin,
|
|
195
|
+
...(resumeSessionId ? ["--resume", sessionId] : ["--session-id", sessionId]),
|
|
196
|
+
...(model?.trim() ? ["--model", model.trim()] : []),
|
|
197
|
+
...cfg.wrc.extraArgs,
|
|
198
|
+
].map((a, i) => (i === 0 ? a : shQuote(a))).join(" ");
|
|
199
|
+
const sent = await runTmux(["send-keys", "-t", tmuxPane, cmd, "Enter"]);
|
|
200
|
+
if (!sent.ok) {
|
|
201
|
+
// Kill only this window/pane, never the shared session.
|
|
202
|
+
await runTmux(["kill-pane", "-t", tmuxPane]);
|
|
203
|
+
return { ok: false, reason: `tmux send-keys failed: ${sent.stderr.trim() || sent.code}` };
|
|
204
|
+
}
|
|
205
|
+
// Settle so the next paste-buffer inject doesn't race claude's TUI startup.
|
|
206
|
+
// Note: claude does NOT create the transcript jsonl until it processes the
|
|
207
|
+
// first user input, so we don't wait for the file here — mirror tail tolerates
|
|
208
|
+
// a missing jsonl and starts emitting once claude writes the first line.
|
|
209
|
+
await sleep(TUI_SETTLE_MS);
|
|
210
|
+
log.info({ tmuxName, tmuxPane, sessionId, jsonlPath, cwd, cli: backend.name }, "spawn-tmux: ready");
|
|
211
|
+
return { ok: true, sessionId, jsonlPath, tmuxPane, tmuxSession: tmuxName, cwd, cli: backend.name };
|
|
212
|
+
};
|
|
213
|
+
//# sourceMappingURL=spawn-tmux.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn-tmux.js","sourceRoot":"","sources":["../../daemon/spawn-tmux.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Rotating one-line hints appended to the `/new` / `/clear` project-info push.
|
|
2
|
+
//
|
|
3
|
+
// Why random-one instead of a fixed "输入 /help": a static line becomes visual
|
|
4
|
+
// noise within a day and stops being read, while the command surface is wide
|
|
5
|
+
// enough that most users never discover half of it. Sampling one tip per
|
|
6
|
+
// session boundary keeps the footer cheap (one line) but eventually exposes
|
|
7
|
+
// everything. `/help` is in the pool too, so the discoverability entry point
|
|
8
|
+
// still surfaces on its own.
|
|
9
|
+
const TIPS = [
|
|
10
|
+
"`/help` 查看全部命令",
|
|
11
|
+
"`#tag` 前缀可在同一聊天并行跑多个会话,如 `#docs 帮我改 README`",
|
|
12
|
+
"`/sessions` 列出所有 live 会话,`/sessions 🐼` 切过去",
|
|
13
|
+
"`/new codebuddy` 换个 CLI 后端开会话(claude / claude-internal / codebuddy)",
|
|
14
|
+
"`/stop` 打断当前生成,`/n` 补一个回车",
|
|
15
|
+
"`/usage` 看真实订阅额度 %,`/cost` 看 token 成本估算",
|
|
16
|
+
"`/audit` 看本会话(含 subagent)的 token / 成本明细",
|
|
17
|
+
"换项目:让 AI 调 `cd` MCP 工具,然后 `/new` 或 `/clear` 生效",
|
|
18
|
+
"直接发图片即可,会自动粘进 CLI 输入框",
|
|
19
|
+
"引用自己发过的命令再发一次,可以绕开微信的相同文本去重",
|
|
20
|
+
"`订阅 <topic>` + `每天 08:30 广播 <topic> <内容>` 可做定时播报",
|
|
21
|
+
"`/pwd` 看当前项目路径,`/id` 看会话 / 权限 id",
|
|
22
|
+
];
|
|
23
|
+
/** One random tip, already formatted as a markdown quote line. */
|
|
24
|
+
export const randomTip = () => `> 💡 ${TIPS[Math.floor(Math.random() * TIPS.length)]}`;
|
|
25
|
+
//# sourceMappingURL=tips.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tips.js","sourceRoot":"","sources":["../../daemon/tips.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,yEAAyE;AACzE,4EAA4E;AAC5E,6EAA6E;AAC7E,6BAA6B;AAE7B,MAAM,IAAI,GAAsB;IAC9B,gBAAgB;IAChB,6CAA6C;IAC7C,6CAA6C;IAC7C,qEAAqE;IACrE,2BAA2B;IAC3B,yCAAyC;IACzC,yCAAyC;IACzC,gDAAgD;IAChD,uBAAuB;IACvB,6BAA6B;IAC7B,kDAAkD;IAClD,kCAAkC;CACnC,CAAC;AAEF,kEAAkE;AAClE,MAAM,CAAC,MAAM,SAAS,GAAG,GAAW,EAAE,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAE,EAAE,CAAC"}
|