weclaude 0.0.4

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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/cli/wrc.sh +168 -0
  4. package/commands/wrc.md +7 -0
  5. package/config.example.jsonc +75 -0
  6. package/dist/cli/init.js +216 -0
  7. package/dist/cli/init.js.map +1 -0
  8. package/dist/cli/sync.js +130 -0
  9. package/dist/cli/sync.js.map +1 -0
  10. package/dist/daemon/approval.js +366 -0
  11. package/dist/daemon/approval.js.map +1 -0
  12. package/dist/daemon/ask.js +97 -0
  13. package/dist/daemon/ask.js.map +1 -0
  14. package/dist/daemon/cc-bridge.js +173 -0
  15. package/dist/daemon/cc-bridge.js.map +1 -0
  16. package/dist/daemon/claim.js +76 -0
  17. package/dist/daemon/claim.js.map +1 -0
  18. package/dist/daemon/http.js +82 -0
  19. package/dist/daemon/http.js.map +1 -0
  20. package/dist/daemon/inbound.js +145 -0
  21. package/dist/daemon/inbound.js.map +1 -0
  22. package/dist/daemon/index.js +85 -0
  23. package/dist/daemon/index.js.map +1 -0
  24. package/dist/daemon/mirror-bridge.js +539 -0
  25. package/dist/daemon/mirror-bridge.js.map +1 -0
  26. package/dist/daemon/outbound.js +33 -0
  27. package/dist/daemon/outbound.js.map +1 -0
  28. package/dist/daemon/pending.js +27 -0
  29. package/dist/daemon/pending.js.map +1 -0
  30. package/dist/daemon/redact.js +27 -0
  31. package/dist/daemon/redact.js.map +1 -0
  32. package/dist/daemon/session-cache.js +66 -0
  33. package/dist/daemon/session-cache.js.map +1 -0
  34. package/dist/daemon/sessions.js +35 -0
  35. package/dist/daemon/sessions.js.map +1 -0
  36. package/dist/daemon/ws.js +67 -0
  37. package/dist/daemon/ws.js.map +1 -0
  38. package/dist/mcp/server.js +82 -0
  39. package/dist/mcp/server.js.map +1 -0
  40. package/dist/shared/config-writer.js +39 -0
  41. package/dist/shared/config-writer.js.map +1 -0
  42. package/dist/shared/config.js +139 -0
  43. package/dist/shared/config.js.map +1 -0
  44. package/dist/shared/log.js +18 -0
  45. package/dist/shared/log.js.map +1 -0
  46. package/dist/shared/paths.js +6 -0
  47. package/dist/shared/paths.js.map +1 -0
  48. package/docs/DESIGN-INIT.md +125 -0
  49. package/docs/ONBOARDING.md +118 -0
  50. package/hooks/hooks.json +16 -0
  51. package/hooks/pre-tool-use.sh +81 -0
  52. package/launchd/com.cc-wecom.daemon.plist.template +45 -0
  53. package/package.json +77 -0
  54. package/scripts/install.sh +50 -0
  55. package/scripts/uninstall.sh +26 -0
  56. package/systemd/cc-wecom.service.template +17 -0
@@ -0,0 +1,173 @@
1
+ // Headless Claude Code bridge. Spawns `claude -p` per WeCom message, streams
2
+ // stdout (stream-json) back via WeCom replyStream. One in-flight subprocess
3
+ // per principal; subsequent messages queue.
4
+ import { spawn } from "node:child_process";
5
+ import { createInterface } from "node:readline";
6
+ import { dirname } from "node:path";
7
+ import { expandHome } from "../shared/paths.js";
8
+ // launchd 默认 PATH 不含 nvm / homebrew,spawn `claude-internal` 会 ENOENT。
9
+ // 用 daemon 自身 Node 的 bin 目录(nvm 装的 cli 通常和 node 同目录)+ 常见路径补齐。
10
+ const NODE_BIN_DIR = dirname(process.execPath);
11
+ const augmentedPath = (orig) => {
12
+ const extras = [
13
+ NODE_BIN_DIR,
14
+ "/opt/homebrew/bin",
15
+ "/usr/local/bin",
16
+ `${process.env.HOME ?? ""}/.local/bin`,
17
+ ].filter(Boolean);
18
+ const seen = new Set();
19
+ return [orig ?? "", ...extras]
20
+ .flatMap((p) => p.split(":"))
21
+ .filter((p) => {
22
+ if (!p || seen.has(p))
23
+ return false;
24
+ seen.add(p);
25
+ return true;
26
+ })
27
+ .join(":");
28
+ };
29
+ const FLUSH_INTERVAL_MS = 250;
30
+ const extractText = (line) => {
31
+ if (line.type === "assistant" && line.message?.content) {
32
+ return line.message.content
33
+ .filter((b) => b.type === "text" && typeof b.text === "string")
34
+ .map((b) => b.text)
35
+ .join("");
36
+ }
37
+ if (line.type === "stream_event" &&
38
+ line.event?.type === "content_block_delta" &&
39
+ line.event.delta?.type === "text_delta") {
40
+ return line.event.delta.text ?? "";
41
+ }
42
+ return undefined;
43
+ };
44
+ const runOne = async (args) => {
45
+ const { text, resumeSid, cfg, log, client, frame, streamId, onSession } = args;
46
+ const cliArgs = [
47
+ "-p",
48
+ text,
49
+ "--output-format",
50
+ "stream-json",
51
+ "--verbose",
52
+ ...cfg.wrc.extraArgs,
53
+ ];
54
+ if (resumeSid)
55
+ cliArgs.push("--resume", resumeSid);
56
+ log.info({ resumeSid, claudeBin: cfg.wrc.claudeBin }, "spawn claude");
57
+ const proc = spawn(cfg.wrc.claudeBin, cliArgs, {
58
+ cwd: expandHome(cfg.wrc.cwd),
59
+ env: { ...process.env, PATH: augmentedPath(process.env.PATH) },
60
+ stdio: ["ignore", "pipe", "pipe"],
61
+ });
62
+ let acc = "";
63
+ let lastSent = "";
64
+ let flushTimer;
65
+ let stderrTail = "";
66
+ const sendFlush = async (finish) => {
67
+ if (acc === lastSent && !finish)
68
+ return;
69
+ lastSent = acc;
70
+ try {
71
+ await client.replyStream(frame, streamId, acc || " ", finish);
72
+ }
73
+ catch (e) {
74
+ log.warn({ err: e.message, finish }, "replyStream failed");
75
+ }
76
+ };
77
+ const scheduleFlush = () => {
78
+ if (flushTimer)
79
+ return;
80
+ flushTimer = setTimeout(() => {
81
+ flushTimer = undefined;
82
+ void sendFlush(false);
83
+ }, FLUSH_INTERVAL_MS);
84
+ };
85
+ const stdout = proc.stdout;
86
+ const stderr = proc.stderr;
87
+ if (!stdout || !stderr) {
88
+ throw new Error("claude subprocess missing stdio pipes");
89
+ }
90
+ // ENOENT / EACCES 等 spawn 失败走 'error' 事件,不监听会 crash 整个 daemon。
91
+ let spawnError;
92
+ proc.on("error", (err) => {
93
+ spawnError = err;
94
+ log.error({ err: err.message, code: err.code }, "spawn error");
95
+ });
96
+ const rl = createInterface({ input: stdout });
97
+ rl.on("line", (raw) => {
98
+ if (!raw.trim())
99
+ return;
100
+ let line;
101
+ try {
102
+ line = JSON.parse(raw);
103
+ }
104
+ catch {
105
+ log.debug({ raw: raw.slice(0, 200) }, "non-JSON stdout line");
106
+ return;
107
+ }
108
+ if (line.type === "system" && line.subtype === "init" && line.session_id) {
109
+ onSession(line.session_id);
110
+ return;
111
+ }
112
+ const piece = extractText(line);
113
+ if (piece) {
114
+ acc += piece;
115
+ scheduleFlush();
116
+ }
117
+ if (line.type === "result") {
118
+ // result.result is the final assistant text in cases where no assistant event was emitted (e.g. no_response).
119
+ if (!acc && typeof line.result === "string")
120
+ acc = line.result;
121
+ }
122
+ });
123
+ proc.stderr?.on("data", (chunk) => {
124
+ const s = chunk.toString("utf8");
125
+ stderrTail = (stderrTail + s).slice(-2000);
126
+ });
127
+ await new Promise((resolve) => {
128
+ proc.on("close", async (code) => {
129
+ if (flushTimer) {
130
+ clearTimeout(flushTimer);
131
+ flushTimer = undefined;
132
+ }
133
+ if (spawnError || code !== 0) {
134
+ const reason = spawnError
135
+ ? `spawn ${cfg.wrc.claudeBin}: ${spawnError.message}`
136
+ : `claude exited ${code}`;
137
+ log.error({ code, spawnErr: spawnError?.message, stderrTail: stderrTail.slice(-500) }, "claude failure");
138
+ const fallback = acc
139
+ ? `${acc}\n\n[${reason}]`
140
+ : `[cc-wecom] ${reason}\n${stderrTail.slice(-500)}`;
141
+ acc = fallback;
142
+ }
143
+ await sendFlush(true);
144
+ log.info({ code, chars: acc.length }, "claude done");
145
+ resolve();
146
+ });
147
+ });
148
+ };
149
+ const queues = new Map();
150
+ const enqueue = (key, job) => {
151
+ const prev = queues.get(key) ?? Promise.resolve();
152
+ const next = prev.then(job, job).catch(() => undefined);
153
+ queues.set(key, next.finally(() => {
154
+ if (queues.get(key) === next)
155
+ queues.delete(key);
156
+ }));
157
+ return next;
158
+ };
159
+ export const makeBridge = (deps) => {
160
+ return {
161
+ dispatch: ({ principal, text, frame, streamId }) => enqueue(principal, () => runOne({
162
+ text,
163
+ resumeSid: deps.sessions.get(principal),
164
+ cfg: deps.cfg,
165
+ log: deps.log.child({ principal }),
166
+ client: deps.client,
167
+ frame,
168
+ streamId,
169
+ onSession: (sid) => deps.sessions.set(principal, sid),
170
+ })),
171
+ };
172
+ };
173
+ //# sourceMappingURL=cc-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cc-bridge.js","sourceRoot":"","sources":["../../daemon/cc-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import { patchJsonc, appendUnique } from "../shared/config-writer.js";
2
+ import { json, readBody } from "./http.js";
3
+ let state;
4
+ const now = () => Date.now();
5
+ const isActive = () => !!state && !state.claimed && state.expiresAt > now();
6
+ /** Returns true iff this message consumed the claim. Caller must skip allowFrom check. */
7
+ export const tryConsumeClaim = (text, principal) => {
8
+ if (!isActive())
9
+ return false;
10
+ if (text.trim() !== state.phrase)
11
+ return false;
12
+ state.claimed = { principal, at: now() };
13
+ return true;
14
+ };
15
+ /** Persist principal as defaultChat + add to allowFrom. Mutates `cfg` in place too. */
16
+ export const persistClaim = (cfg, sourcePath, principal) => {
17
+ cfg.defaultChat = principal;
18
+ if (!cfg.wrc.allowFrom.includes(principal))
19
+ cfg.wrc.allowFrom.push(principal);
20
+ patchJsonc(sourcePath, [{ path: ["defaultChat"], value: principal }]);
21
+ appendUnique(sourcePath, ["wrc", "allowFrom"], principal);
22
+ };
23
+ const principalChatId = (p) => {
24
+ const i = p.indexOf(":");
25
+ return i >= 0 ? p.slice(i + 1) : p;
26
+ };
27
+ export const makeClaimStartHandler = ({ log }) => {
28
+ return async (req, res) => {
29
+ const body = (await readBody(req));
30
+ const phrase = (body.phrase ?? "").trim();
31
+ const ttlSec = Math.max(30, Math.min(body.ttlSec ?? 600, 1800));
32
+ if (!phrase) {
33
+ json(res, 400, { ok: false, error: "phrase required" });
34
+ return;
35
+ }
36
+ state = { phrase, expiresAt: now() + ttlSec * 1000 };
37
+ log.info({ phrase, ttlSec }, "claim mode armed");
38
+ json(res, 200, { ok: true, expiresAt: state.expiresAt });
39
+ };
40
+ };
41
+ export const makeClaimStatusHandler = () => {
42
+ return (_req, res) => {
43
+ if (!state) {
44
+ json(res, 200, { armed: false });
45
+ return;
46
+ }
47
+ json(res, 200, {
48
+ armed: !state.claimed && state.expiresAt > now(),
49
+ expired: !state.claimed && state.expiresAt <= now(),
50
+ claimed: state.claimed ?? null,
51
+ phrase: state.phrase,
52
+ });
53
+ };
54
+ };
55
+ export const makeClaimResetHandler = () => {
56
+ return (_req, res) => {
57
+ state = undefined;
58
+ json(res, 200, { ok: true });
59
+ };
60
+ };
61
+ /** Send a markdown ack to the user that just completed the claim. */
62
+ export const ackClaim = async (client, principal, log) => {
63
+ try {
64
+ await client.sendMessage(principalChatId(principal), {
65
+ msgtype: "markdown",
66
+ markdown: {
67
+ content: "✅ **已设为默认会话**\n\n" +
68
+ "你已被加入 `allowFrom`。接下来 `wrc init` 会触发一个真实授权演示——留意按钮卡片。",
69
+ },
70
+ });
71
+ }
72
+ catch (e) {
73
+ log.warn({ err: e.message }, "claim ack failed");
74
+ }
75
+ };
76
+ //# sourceMappingURL=claim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim.js","sourceRoot":"","sources":["../../daemon/claim.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ // Local control HTTP — talks to hooks, slash commands, MCP server.
2
+ // Bound to 127.0.0.1; trust = "anyone with loopback access".
3
+ // Endpoints intentionally tiny & sync-friendly.
4
+ import { createServer } from "node:http";
5
+ import { URL } from "node:url";
6
+ import { listPending } from "./pending.js";
7
+ const json = (res, status, body) => {
8
+ res.statusCode = status;
9
+ res.setHeader("content-type", "application/json");
10
+ res.end(JSON.stringify(body));
11
+ };
12
+ const readBody = async (req) => {
13
+ const chunks = [];
14
+ for await (const c of req)
15
+ chunks.push(c);
16
+ if (chunks.length === 0)
17
+ return {};
18
+ try {
19
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
20
+ }
21
+ catch {
22
+ return {};
23
+ }
24
+ };
25
+ const routes = {
26
+ "GET /healthz": (_req, res, _url, { ws }) => {
27
+ json(res, 200, { ok: true, wsConnected: ws.client.isConnected });
28
+ },
29
+ "GET /status": (_req, res, _url, { cfg, ws, sourcePath }) => {
30
+ json(res, 200, {
31
+ ok: true,
32
+ wsConnected: ws.client.isConnected,
33
+ botId: cfg.bot.botId,
34
+ defaultChat: cfg.defaultChat,
35
+ pending: listPending().length,
36
+ uptimeSec: Math.round(process.uptime()),
37
+ sourcePath,
38
+ logFile: cfg.daemon.logFile,
39
+ });
40
+ },
41
+ "GET /pending": (_req, res) => {
42
+ json(res, 200, { pending: listPending() });
43
+ },
44
+ "POST /shutdown": async (_req, res, _url, { ws, log }) => {
45
+ json(res, 200, { ok: true });
46
+ log.info("shutdown via HTTP");
47
+ await ws.shutdown();
48
+ setTimeout(() => process.exit(0), 100);
49
+ },
50
+ // /approve and /message are wired by callers later (approval, slash).
51
+ };
52
+ export const startHttp = (ctx) => {
53
+ const dynamic = new Map();
54
+ const server = createServer(async (req, res) => {
55
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
56
+ const key = `${req.method ?? "GET"} ${url.pathname}`;
57
+ const handler = dynamic.get(key) ?? routes[key];
58
+ if (!handler) {
59
+ json(res, 404, { error: "not_found", route: key });
60
+ return;
61
+ }
62
+ try {
63
+ await handler(req, res, url, ctx);
64
+ }
65
+ catch (e) {
66
+ ctx.log.error({ err: e.message, route: key }, "http handler failed");
67
+ if (!res.headersSent)
68
+ json(res, 500, { error: "internal", message: e.message });
69
+ }
70
+ });
71
+ server.listen(ctx.cfg.daemon.port, ctx.cfg.daemon.host, () => {
72
+ ctx.log.info({ host: ctx.cfg.daemon.host, port: ctx.cfg.daemon.port }, "HTTP listening");
73
+ });
74
+ return {
75
+ close: () => new Promise((resolve) => {
76
+ server.close(() => resolve());
77
+ }),
78
+ register: (key, handler) => dynamic.set(key, handler),
79
+ };
80
+ };
81
+ export { readBody, json };
82
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../daemon/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ // Inbound text router. Hands the message off to either the headless CC bridge
2
+ // (mode=headless) or the mirror bridge (mode=mirror).
3
+ import { mkdirSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { expandHome } from "../shared/paths.js";
6
+ import { tryConsumeClaim, persistClaim, ackClaim } from "./claim.js";
7
+ const principal = (msg) => msg.chattype === "group" && msg.chatid ? `chat:${msg.chatid}` : `user:${msg.from.userid}`;
8
+ const isAllowed = (cfg, who) => {
9
+ if (cfg.wrc.allowFrom.length === 0)
10
+ return false;
11
+ return cfg.wrc.allowFrom.includes(who);
12
+ };
13
+ // Sniff extension from magic bytes; falls back to .bin. WeCom doesn't always
14
+ // give us a filename for images, and we want claude's Read tool to recognize
15
+ // the file (it dispatches on extension).
16
+ const sniffExt = (buf) => {
17
+ if (buf.length >= 8 && buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47)
18
+ return ".png";
19
+ if (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff)
20
+ return ".jpg";
21
+ if (buf.length >= 6 && buf.subarray(0, 6).toString("ascii").startsWith("GIF8"))
22
+ return ".gif";
23
+ if (buf.length >= 12 && buf.subarray(0, 4).toString("ascii") === "RIFF" && buf.subarray(8, 12).toString("ascii") === "WEBP")
24
+ return ".webp";
25
+ if (buf.length >= 12 && buf.subarray(4, 12).toString("ascii") === "ftypheic")
26
+ return ".heic";
27
+ return ".bin";
28
+ };
29
+ const downloadToInbox = async (deps, url, aesKey, msgid, index) => {
30
+ try {
31
+ const { buffer, filename } = await deps.client.downloadFile(url, aesKey);
32
+ const ext = filename ? `.${filename.split(".").pop()}` : sniffExt(buffer);
33
+ const safeName = `${msgid.replace(/[^A-Za-z0-9_-]/g, "_")}_${index}${ext}`;
34
+ mkdirSync(deps.inboxDir, { recursive: true });
35
+ const abs = join(deps.inboxDir, safeName);
36
+ writeFileSync(abs, buffer);
37
+ deps.log.info({ url: url.slice(0, 80), bytes: buffer.length, abs }, "media saved");
38
+ return abs;
39
+ }
40
+ catch (e) {
41
+ deps.log.error({ err: e.message }, "media download failed");
42
+ return undefined;
43
+ }
44
+ };
45
+ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
46
+ const inboxDir = expandHome(cfg.wrc.mirror.inboxDir);
47
+ // Common gating: claim bootstrap + allowFrom check. Returns true if the
48
+ // caller should stop (claim consumed or message rejected).
49
+ const gate = async (frame, msg, text) => {
50
+ const who = principal(msg);
51
+ if (tryConsumeClaim(text, who)) {
52
+ log.info({ who }, "claim consumed — bootstrapping defaultChat + allowFrom");
53
+ try {
54
+ persistClaim(cfg, sourcePath, who);
55
+ }
56
+ catch (e) {
57
+ log.error({ err: e.message }, "persistClaim failed");
58
+ }
59
+ await ackClaim(client, who, log);
60
+ try {
61
+ await client.replyStream(frame, msg.msgid, "✅ done", true);
62
+ }
63
+ catch { /* ignore */ }
64
+ return { stop: true, who };
65
+ }
66
+ if (!isAllowed(cfg, who)) {
67
+ log.warn({ from: who }, "drop: not in allowFrom");
68
+ try {
69
+ await client.replyStream(frame, msg.msgid, "[cc-wecom] 未授权(请在 config 的 wrc.allowFrom 加入你的 user:<id>)", true);
70
+ }
71
+ catch { /* ignore */ }
72
+ return { stop: true, who };
73
+ }
74
+ return { stop: false, who };
75
+ };
76
+ const send = async (frame, msg, who, text) => {
77
+ try {
78
+ await bridge.dispatch({ principal: who, text, frame, streamId: msg.msgid });
79
+ }
80
+ catch (e) {
81
+ log.error({ err: e.message }, "bridge dispatch failed");
82
+ try {
83
+ await client.replyStream(frame, msg.msgid, `[cc-wecom] error: ${e.message}`, true);
84
+ }
85
+ catch { /* ignore */ }
86
+ }
87
+ };
88
+ client.on("message.text", async (frame) => {
89
+ const msg = frame.body;
90
+ if (!msg)
91
+ return;
92
+ const text = msg.text?.content ?? "";
93
+ log.info({ msgid: msg.msgid, len: text.length }, "rx text");
94
+ const { stop, who } = await gate(frame, msg, text);
95
+ if (stop)
96
+ return;
97
+ await send(frame, msg, who, text);
98
+ });
99
+ client.on("message.image", async (frame) => {
100
+ const msg = frame.body;
101
+ if (!msg)
102
+ return;
103
+ log.info({ msgid: msg.msgid }, "rx image");
104
+ const { stop, who } = await gate(frame, msg, "");
105
+ if (stop)
106
+ return;
107
+ const path = await downloadToInbox({ client, log, inboxDir }, msg.image.url, msg.image.aeskey, msg.msgid, 0);
108
+ if (!path) {
109
+ try {
110
+ await client.replyStream(frame, msg.msgid, "[cc-wecom] 图片下载失败", true);
111
+ }
112
+ catch { /* ignore */ }
113
+ return;
114
+ }
115
+ // Bare path → claude attaches the image (its TUI accepts absolute file paths
116
+ // dropped/pasted into the input box).
117
+ await send(frame, msg, who, path);
118
+ });
119
+ client.on("message.mixed", async (frame) => {
120
+ const msg = frame.body;
121
+ if (!msg)
122
+ return;
123
+ log.info({ msgid: msg.msgid, items: msg.mixed?.msg_item?.length }, "rx mixed");
124
+ const { stop, who } = await gate(frame, msg, "");
125
+ if (stop)
126
+ return;
127
+ const parts = [];
128
+ let imgIdx = 0;
129
+ for (const item of msg.mixed?.msg_item ?? []) {
130
+ if (item.msgtype === "text" && item.text?.content) {
131
+ parts.push(item.text.content);
132
+ }
133
+ else if (item.msgtype === "image" && item.image?.url) {
134
+ const path = await downloadToInbox({ client, log, inboxDir }, item.image.url, item.image.aeskey, msg.msgid, imgIdx++);
135
+ if (path)
136
+ parts.push(path);
137
+ }
138
+ }
139
+ if (parts.length === 0)
140
+ return;
141
+ await send(frame, msg, who, parts.join("\n"));
142
+ });
143
+ // template_card_event is handled in approval module; no listener here.
144
+ };
145
+ //# sourceMappingURL=inbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../daemon/inbound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ // Daemon entry. Resident process — exits only on signal or fatal WS auth failure.
2
+ import { loadConfig } from "../shared/config.js";
3
+ import { makeLogger } from "../shared/log.js";
4
+ import { startWs } from "./ws.js";
5
+ import { startHttp, json } from "./http.js";
6
+ import { installInboundRouter } from "./inbound.js";
7
+ import { loadSessionStore } from "./sessions.js";
8
+ import { makeBridge } from "./cc-bridge.js";
9
+ import { startMirror } from "./mirror-bridge.js";
10
+ import { installApprovalEventListener, makeApproveHandler } from "./approval.js";
11
+ import { makeMessageHandler } from "./outbound.js";
12
+ import { makeCardHandler, makeAskHandler, installAskEventListener } from "./ask.js";
13
+ import { makeClaimStartHandler, makeClaimStatusHandler, makeClaimResetHandler, } from "./claim.js";
14
+ // pino's file transport is async; without flush, log.fatal before process.exit
15
+ // vanishes. Mirror anything fatal to stderr too so launchd's stderr.log captures it.
16
+ const fatalExit = (msg, extra) => {
17
+ // eslint-disable-next-line no-console
18
+ console.error(`[wrc-daemon] FATAL: ${msg}`, extra ?? "");
19
+ process.exit(1);
20
+ };
21
+ const main = async () => {
22
+ const { config: cfg, sourcePath } = loadConfig();
23
+ const log = makeLogger({
24
+ logFile: cfg.daemon.logFile,
25
+ logLevel: cfg.daemon.logLevel,
26
+ name: "wrc-daemon",
27
+ });
28
+ log.info({ sourcePath, pid: process.pid }, "daemon start");
29
+ const ws = startWs(cfg, log);
30
+ const sessions = loadSessionStore(cfg.wrc.sessionMapFile);
31
+ const bridge = cfg.wrc.mode === "mirror"
32
+ ? startMirror({ cfg, log: log.child({ mod: "mirror" }), client: ws.client })
33
+ : makeBridge({ cfg, log: log.child({ mod: "bridge" }), client: ws.client, sessions });
34
+ if (!bridge) {
35
+ log.fatal("bridge failed to start");
36
+ fatalExit("bridge failed to start");
37
+ }
38
+ installInboundRouter(ws.client, cfg, log, bridge, sourcePath);
39
+ installApprovalEventListener(ws.client, log.child({ mod: "approval" }), cfg);
40
+ const http = startHttp({ cfg, ws, log, sourcePath });
41
+ http.register("POST /approve", makeApproveHandler({ cfg, log: log.child({ mod: "approval" }), client: ws.client }));
42
+ http.register("POST /message", makeMessageHandler(ws.client, log.child({ mod: "outbound" })));
43
+ http.register("POST /card", makeCardHandler(ws.client, log.child({ mod: "outbound" })));
44
+ http.register("POST /ask", makeAskHandler(ws.client, log.child({ mod: "ask" })));
45
+ http.register("POST /claim/start", makeClaimStartHandler({ log: log.child({ mod: "claim" }) }));
46
+ http.register("GET /claim/status", makeClaimStatusHandler());
47
+ http.register("POST /claim/reset", makeClaimResetHandler());
48
+ installAskEventListener(ws.client, log.child({ mod: "ask" }));
49
+ // Mirror-mode: expose attach/status so a slash command can pin the live session.
50
+ if (cfg.wrc.mode === "mirror") {
51
+ const m = bridge;
52
+ http.register("GET /mirror/status", (_req, res) => json(res, 200, m.status()));
53
+ http.register("POST /mirror/attach", async (req, res) => {
54
+ const { readBody } = await import("./http.js");
55
+ const body = (await readBody(req));
56
+ if (!body.sessionId || !body.jsonlPath) {
57
+ json(res, 400, { ok: false, reason: "sessionId and jsonlPath required" });
58
+ return;
59
+ }
60
+ const r = m.attach({ sessionId: body.sessionId, jsonlPath: body.jsonlPath, target: body.target });
61
+ json(res, r.ok ? 200 : 400, r);
62
+ });
63
+ }
64
+ const shutdown = async (signal) => {
65
+ log.info({ signal }, "shutdown signal");
66
+ await Promise.allSettled([ws.shutdown(), http.close()]);
67
+ process.exit(0);
68
+ };
69
+ process.on("SIGINT", () => void shutdown("SIGINT"));
70
+ process.on("SIGTERM", () => void shutdown("SIGTERM"));
71
+ try {
72
+ await ws.ready;
73
+ log.info("daemon ready");
74
+ }
75
+ catch (e) {
76
+ log.fatal({ err: e.message }, "WS fatal — exiting");
77
+ fatalExit("WS fatal", { err: e.message });
78
+ }
79
+ };
80
+ main().catch((e) => {
81
+ // eslint-disable-next-line no-console
82
+ console.error("[wrc-daemon] fatal:", e);
83
+ process.exit(1);
84
+ });
85
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../daemon/index.ts"],"names":[],"mappings":"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"}