wechat-claude-sessions 1.0.0
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/CONTRIBUTING.md +52 -0
- package/LICENSE +21 -0
- package/README.en.md +264 -0
- package/README.md +245 -0
- package/SECURITY.md +45 -0
- package/dist/bindings.d.ts +4 -0
- package/dist/bindings.js +50 -0
- package/dist/bindings.js.map +1 -0
- package/dist/claude-config.d.ts +4 -0
- package/dist/claude-config.js +49 -0
- package/dist/claude-config.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +345 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon.d.ts +2 -0
- package/dist/daemon.js +771 -0
- package/dist/daemon.js.map +1 -0
- package/dist/i18n.d.ts +67 -0
- package/dist/i18n.js +249 -0
- package/dist/i18n.js.map +1 -0
- package/dist/ilink.d.ts +49 -0
- package/dist/ilink.js +533 -0
- package/dist/ilink.js.map +1 -0
- package/dist/inbox.d.ts +4 -0
- package/dist/inbox.js +75 -0
- package/dist/inbox.js.map +1 -0
- package/dist/launchd.d.ts +7 -0
- package/dist/launchd.js +71 -0
- package/dist/launchd.js.map +1 -0
- package/dist/monitoring.d.ts +4 -0
- package/dist/monitoring.js +33 -0
- package/dist/monitoring.js.map +1 -0
- package/dist/paths.d.ts +11 -0
- package/dist/paths.js +35 -0
- package/dist/paths.js.map +1 -0
- package/dist/pkg-root.d.ts +2 -0
- package/dist/pkg-root.js +11 -0
- package/dist/pkg-root.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +364 -0
- package/dist/server.js.map +1 -0
- package/dist/session-numbers.d.ts +1 -0
- package/dist/session-numbers.js +44 -0
- package/dist/session-numbers.js.map +1 -0
- package/dist/sessions.d.ts +15 -0
- package/dist/sessions.js +135 -0
- package/dist/sessions.js.map +1 -0
- package/dist/tmux.d.ts +7 -0
- package/dist/tmux.js +47 -0
- package/dist/tmux.js.map +1 -0
- package/dist/types.d.ts +150 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +88 -0
- package/dist/utils.js.map +1 -0
- package/dist/watch-inbox.d.ts +2 -0
- package/dist/watch-inbox.js +102 -0
- package/dist/watch-inbox.js.map +1 -0
- package/package.json +50 -0
- package/templates/com.wechat-claude.daemon.plist.template +31 -0
- package/templates/wechat.md +20 -0
package/dist/launchd.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// launchd service management (macOS). Generates the daemon plist from the
|
|
2
|
+
// packaged template, filling in this machine's node binary, install path, log
|
|
3
|
+
// path, and PATH — so it works the same whether the package was installed
|
|
4
|
+
// globally from npm or built from a git checkout.
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { pkgFile } from "./pkg-root.js";
|
|
11
|
+
import { ensureDirs, WECHAT_DIR } from "./paths.js";
|
|
12
|
+
export const LABEL = "com.wechat-claude.daemon";
|
|
13
|
+
export const PLIST_PATH = path.join(os.homedir(), "Library", "LaunchAgents", `${LABEL}.plist`);
|
|
14
|
+
// Include the node binary's own directory plus common tool locations so the
|
|
15
|
+
// daemon can find `claude`, `tmux`, etc. under launchd's minimal environment.
|
|
16
|
+
function daemonPath() {
|
|
17
|
+
const pathEntries = [
|
|
18
|
+
path.dirname(process.execPath),
|
|
19
|
+
path.join(os.homedir(), ".local/bin"),
|
|
20
|
+
"/opt/homebrew/bin",
|
|
21
|
+
"/usr/local/bin",
|
|
22
|
+
"/usr/bin",
|
|
23
|
+
"/bin",
|
|
24
|
+
];
|
|
25
|
+
return [...new Set(pathEntries)].join(":");
|
|
26
|
+
}
|
|
27
|
+
// The plist is XML: a home directory or install path containing & or < would
|
|
28
|
+
// otherwise produce a malformed file that launchctl refuses to load.
|
|
29
|
+
function escapeXml(value) {
|
|
30
|
+
return value
|
|
31
|
+
.replaceAll("&", "&")
|
|
32
|
+
.replaceAll("<", "<")
|
|
33
|
+
.replaceAll(">", ">");
|
|
34
|
+
}
|
|
35
|
+
export function writePlist() {
|
|
36
|
+
// launchd refuses to start the job if StandardOutPath's parent is missing —
|
|
37
|
+
// `daemon install` can legitimately run before `setup` has created the tree.
|
|
38
|
+
ensureDirs();
|
|
39
|
+
const template = pkgFile("com.wechat-claude.daemon.plist.template");
|
|
40
|
+
const daemonJs = fileURLToPath(new URL("./daemon.js", import.meta.url));
|
|
41
|
+
const logPath = path.join(WECHAT_DIR, "daemon.log");
|
|
42
|
+
const filled = fs
|
|
43
|
+
.readFileSync(template, "utf-8")
|
|
44
|
+
.replaceAll("__NODE__", escapeXml(process.execPath))
|
|
45
|
+
.replaceAll("__DAEMON_JS__", escapeXml(daemonJs))
|
|
46
|
+
.replaceAll("__LOG__", escapeXml(logPath))
|
|
47
|
+
.replaceAll("__PATH__", escapeXml(daemonPath()));
|
|
48
|
+
fs.mkdirSync(path.dirname(PLIST_PATH), { recursive: true });
|
|
49
|
+
fs.writeFileSync(PLIST_PATH, filled);
|
|
50
|
+
return PLIST_PATH;
|
|
51
|
+
}
|
|
52
|
+
function launchctl(...args) {
|
|
53
|
+
return spawnSync("launchctl", args, { stdio: "ignore" }).status === 0;
|
|
54
|
+
}
|
|
55
|
+
export function isMac() {
|
|
56
|
+
return process.platform === "darwin";
|
|
57
|
+
}
|
|
58
|
+
export function load() {
|
|
59
|
+
// Unload first so `install` is idempotent — a reload picks up a plist whose
|
|
60
|
+
// node path changed (nvm switch, npm -g upgrade to a new prefix).
|
|
61
|
+
launchctl("unload", PLIST_PATH);
|
|
62
|
+
return launchctl("load", PLIST_PATH);
|
|
63
|
+
}
|
|
64
|
+
export function unload() {
|
|
65
|
+
return launchctl("unload", PLIST_PATH);
|
|
66
|
+
}
|
|
67
|
+
export function isLoaded() {
|
|
68
|
+
const r = spawnSync("launchctl", ["list"], { encoding: "utf-8" });
|
|
69
|
+
return (r.stdout ?? "").includes(LABEL);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=launchd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launchd.js","sourceRoot":"","sources":["../src/launchd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const HEARTBEAT_DIR = path.join(os.homedir(), ".claude", "wechat", "heartbeat");
|
|
5
|
+
// A session counts as "monitoring" if its watcher touched the heartbeat file
|
|
6
|
+
// within this window. The watcher touches every 10s.
|
|
7
|
+
export const MONITORING_WINDOW_MS = 30_000;
|
|
8
|
+
function heartbeatFile(sessionId) {
|
|
9
|
+
return path.join(HEARTBEAT_DIR, sessionId);
|
|
10
|
+
}
|
|
11
|
+
export function touchHeartbeat(sessionId) {
|
|
12
|
+
try {
|
|
13
|
+
fs.mkdirSync(HEARTBEAT_DIR, { recursive: true });
|
|
14
|
+
fs.writeFileSync(heartbeatFile(sessionId), String(Date.now()));
|
|
15
|
+
}
|
|
16
|
+
catch { }
|
|
17
|
+
}
|
|
18
|
+
export function clearHeartbeat(sessionId) {
|
|
19
|
+
try {
|
|
20
|
+
fs.unlinkSync(heartbeatFile(sessionId));
|
|
21
|
+
}
|
|
22
|
+
catch { }
|
|
23
|
+
}
|
|
24
|
+
export function isMonitoring(sessionId, windowMs = MONITORING_WINDOW_MS) {
|
|
25
|
+
try {
|
|
26
|
+
const ts = parseInt(fs.readFileSync(heartbeatFile(sessionId), "utf-8").trim(), 10);
|
|
27
|
+
return Number.isFinite(ts) && Date.now() - ts < windowMs;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=monitoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitoring.js","sourceRoot":"","sources":["../src/monitoring.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAEhF,6EAA6E;AAC7E,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAE3C,SAAS,aAAa,CAAC,SAAiB;IACtC,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,EAAE,CAAC,aAAa,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,SAAiB,EACjB,WAAmB,oBAAoB;IAEvC,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,QAAQ,CACjB,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,EACzD,EAAE,CACH,CAAC;QACF,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const WECHAT_DIR: string;
|
|
2
|
+
export declare const SESSIONS_DIR: string;
|
|
3
|
+
export declare const INBOX_DIR: string;
|
|
4
|
+
export declare const TYPING_DIR: string;
|
|
5
|
+
export declare const MEDIA_DIR: string;
|
|
6
|
+
export declare const CURSOR_FILE: string;
|
|
7
|
+
export declare const DAEMON_PID_FILE: string;
|
|
8
|
+
export declare const EXPIRED_FLAG_FILE: string;
|
|
9
|
+
export declare const CONFIG_FILE: string;
|
|
10
|
+
export declare function ensureDirs(extra?: string[]): void;
|
|
11
|
+
export declare function isProcessAlive(pid: number): boolean;
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
// Single source of truth for the on-disk layout under ~/.claude/wechat.
|
|
5
|
+
export const WECHAT_DIR = path.join(os.homedir(), ".claude", "wechat");
|
|
6
|
+
export const SESSIONS_DIR = path.join(WECHAT_DIR, "sessions");
|
|
7
|
+
export const INBOX_DIR = path.join(WECHAT_DIR, "inbox");
|
|
8
|
+
export const TYPING_DIR = path.join(WECHAT_DIR, "typing");
|
|
9
|
+
export const MEDIA_DIR = path.join(WECHAT_DIR, "media");
|
|
10
|
+
export const CURSOR_FILE = path.join(WECHAT_DIR, "cursor.txt");
|
|
11
|
+
export const DAEMON_PID_FILE = path.join(WECHAT_DIR, "daemon.pid");
|
|
12
|
+
export const EXPIRED_FLAG_FILE = path.join(WECHAT_DIR, "expired.flag");
|
|
13
|
+
export const CONFIG_FILE = path.join(WECHAT_DIR, "config.json");
|
|
14
|
+
// Create the tree owner-only (0700) — it holds the bot token, context tokens,
|
|
15
|
+
// and downloaded media. chmod (not just mkdir mode) so directories created
|
|
16
|
+
// before this hardening, or with a looser umask, are tightened too.
|
|
17
|
+
export function ensureDirs(extra = []) {
|
|
18
|
+
for (const dir of [WECHAT_DIR, SESSIONS_DIR, INBOX_DIR, TYPING_DIR, ...extra]) {
|
|
19
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
20
|
+
try {
|
|
21
|
+
fs.chmodSync(dir, 0o700);
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function isProcessAlive(pid) {
|
|
27
|
+
try {
|
|
28
|
+
process.kill(pid, 0);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,wEAAwE;AACxE,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AACvE,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;AAC9D,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AACnE,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;AACvE,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEhE,8EAA8E;AAC9E,2EAA2E;AAC3E,oEAAoE;AACpE,MAAM,UAAU,UAAU,CAAC,QAAkB,EAAE;IAC7C,KAAK,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;QAC9E,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC;YACH,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/pkg-root.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
// Root of the installed package (the directory containing dist/), whether that
|
|
4
|
+
// is a global npm install or a git checkout.
|
|
5
|
+
export const PKG_ROOT = path.resolve(fileURLToPath(new URL("..", import.meta.url)));
|
|
6
|
+
// Resolve a file shipped in templates/ — the one location for assets that must
|
|
7
|
+
// reach users, listed in package.json "files" and present in the repo.
|
|
8
|
+
export function pkgFile(relative) {
|
|
9
|
+
return path.join(PKG_ROOT, "templates", relative);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=pkg-root.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkg-root.js","sourceRoot":"","sources":["../src/pkg-root.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,+EAA+E;AAC/E,6CAA6C;AAC7C,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAClC,aAAa,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAC9C,CAAC;AAEF,+EAA+E;AAC/E,uEAAuE;AACvE,MAAM,UAAU,OAAO,CAAC,QAAgB;IACtC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;AACpD,CAAC"}
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { ILinkClient } from "./ilink.js";
|
|
10
|
+
import { PKG_ROOT } from "./pkg-root.js";
|
|
11
|
+
import { peekInbox as peekInboxFor, readInbox as readInboxFor } from "./inbox.js";
|
|
12
|
+
import { isMonitoring, touchHeartbeat } from "./monitoring.js";
|
|
13
|
+
import { DAEMON_PID_FILE, EXPIRED_FLAG_FILE, SESSIONS_DIR, TYPING_DIR, WECHAT_DIR, ensureDirs as ensureWechatDirs, isProcessAlive, } from "./paths.js";
|
|
14
|
+
import { detectSessionName as detectSessionNameFor, listSessions, writeSessionFile, } from "./sessions.js";
|
|
15
|
+
const DAEMON_LOG_FILE = path.join(WECHAT_DIR, "daemon.log");
|
|
16
|
+
const DAEMON_PATH = fileURLToPath(new URL("./daemon.js", import.meta.url));
|
|
17
|
+
// Report the installed package's version rather than a hardcoded one that
|
|
18
|
+
// drifts from package.json. npm always ships package.json, `files` or not.
|
|
19
|
+
const PKG_VERSION = (() => {
|
|
20
|
+
try {
|
|
21
|
+
const raw = fs.readFileSync(path.join(PKG_ROOT, "package.json"), "utf-8");
|
|
22
|
+
return JSON.parse(raw).version ?? "0.0.0";
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return "0.0.0";
|
|
26
|
+
}
|
|
27
|
+
})();
|
|
28
|
+
const WATCHER_PATH = fileURLToPath(new URL("./watch-inbox.js", import.meta.url));
|
|
29
|
+
function ensureDirs() {
|
|
30
|
+
ensureWechatDirs();
|
|
31
|
+
}
|
|
32
|
+
function detectSessionName() {
|
|
33
|
+
return detectSessionNameFor(process.cwd());
|
|
34
|
+
}
|
|
35
|
+
function isDaemonRunning() {
|
|
36
|
+
try {
|
|
37
|
+
const pid = parseInt(fs.readFileSync(DAEMON_PID_FILE, "utf-8").trim(), 10);
|
|
38
|
+
return isProcessAlive(pid);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Spawn the daemon detached if it is not already running. The daemon has its
|
|
45
|
+
// own pid-file singleton guard, so a concurrent spawn from another session is
|
|
46
|
+
// harmless. Returns true if the daemon is running when we're done.
|
|
47
|
+
async function ensureDaemonRunning() {
|
|
48
|
+
if (isDaemonRunning())
|
|
49
|
+
return { running: true, autoStarted: false };
|
|
50
|
+
if (!client.isLoggedIn)
|
|
51
|
+
return { running: false, autoStarted: false };
|
|
52
|
+
try {
|
|
53
|
+
const logFd = fs.openSync(DAEMON_LOG_FILE, "a");
|
|
54
|
+
const child = spawn(process.execPath, [DAEMON_PATH], {
|
|
55
|
+
detached: true,
|
|
56
|
+
stdio: ["ignore", logFd, logFd],
|
|
57
|
+
});
|
|
58
|
+
child.unref();
|
|
59
|
+
fs.closeSync(logFd);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return { running: false, autoStarted: false };
|
|
63
|
+
}
|
|
64
|
+
const deadline = Date.now() + 3000;
|
|
65
|
+
while (Date.now() < deadline) {
|
|
66
|
+
if (isDaemonRunning())
|
|
67
|
+
return { running: true, autoStarted: true };
|
|
68
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
69
|
+
}
|
|
70
|
+
return { running: false, autoStarted: false };
|
|
71
|
+
}
|
|
72
|
+
function isLoginExpired() {
|
|
73
|
+
return fs.existsSync(EXPIRED_FLAG_FILE);
|
|
74
|
+
}
|
|
75
|
+
const sessionId = String(process.pid);
|
|
76
|
+
const sessionName = { value: detectSessionName() };
|
|
77
|
+
const client = new ILinkClient();
|
|
78
|
+
function currentSessionInfo() {
|
|
79
|
+
return {
|
|
80
|
+
id: sessionId,
|
|
81
|
+
name: sessionName.value,
|
|
82
|
+
cwd: process.cwd(),
|
|
83
|
+
pid: process.pid,
|
|
84
|
+
lastActive: Date.now(),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function register() {
|
|
88
|
+
ensureDirs();
|
|
89
|
+
writeSessionFile(currentSessionInfo());
|
|
90
|
+
}
|
|
91
|
+
function unregister() {
|
|
92
|
+
try {
|
|
93
|
+
fs.unlinkSync(path.join(SESSIONS_DIR, `${sessionId}.json`));
|
|
94
|
+
}
|
|
95
|
+
catch { }
|
|
96
|
+
}
|
|
97
|
+
function touchActive() {
|
|
98
|
+
writeSessionFile(currentSessionInfo());
|
|
99
|
+
}
|
|
100
|
+
function readInbox() {
|
|
101
|
+
return readInboxFor(sessionId);
|
|
102
|
+
}
|
|
103
|
+
function peekInbox() {
|
|
104
|
+
return peekInboxFor(sessionId);
|
|
105
|
+
}
|
|
106
|
+
function clearTyping(userId) {
|
|
107
|
+
try {
|
|
108
|
+
fs.unlinkSync(path.join(TYPING_DIR, userId));
|
|
109
|
+
}
|
|
110
|
+
catch { }
|
|
111
|
+
}
|
|
112
|
+
const server = new McpServer({ name: "wechat-claude", version: PKG_VERSION });
|
|
113
|
+
server.tool("wechat_login", "Login to WeChat by scanning a QR code.", {}, async () => {
|
|
114
|
+
if (client.isLoggedIn) {
|
|
115
|
+
return {
|
|
116
|
+
content: [
|
|
117
|
+
{ type: "text", text: "Already logged in. Use wechat_logout first." },
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const qr = await client.getQRCode();
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: `QR code generated. Ask the user to scan it with WeChat.\n\nQR Code URL: ${qr.qrcode_img_content}\n\nUse wechat_login_poll with qrcode_token="${qr.qrcode}" to check scan status.`,
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
return {
|
|
134
|
+
content: [
|
|
135
|
+
{
|
|
136
|
+
type: "text",
|
|
137
|
+
text: `Login failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
isError: true,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
server.tool("wechat_login_poll", "Poll QR code scan status until 'confirmed'.", { qrcode_token: z.string().describe("The qrcode token from wechat_login") }, async ({ qrcode_token }) => {
|
|
145
|
+
try {
|
|
146
|
+
const status = await client.pollQRCodeStatus(qrcode_token);
|
|
147
|
+
if (status.status === "confirmed" &&
|
|
148
|
+
status.bot_token &&
|
|
149
|
+
status.ilink_bot_id &&
|
|
150
|
+
status.ilink_user_id) {
|
|
151
|
+
client.setSession({
|
|
152
|
+
botToken: status.bot_token,
|
|
153
|
+
ilinkBotId: status.ilink_bot_id,
|
|
154
|
+
ilinkUserId: status.ilink_user_id,
|
|
155
|
+
baseUrl: status.baseurl || "https://ilinkai.weixin.qq.com",
|
|
156
|
+
});
|
|
157
|
+
try {
|
|
158
|
+
fs.unlinkSync(EXPIRED_FLAG_FILE);
|
|
159
|
+
}
|
|
160
|
+
catch { }
|
|
161
|
+
const daemon = await ensureDaemonRunning();
|
|
162
|
+
const daemonNote = daemon.running
|
|
163
|
+
? daemon.autoStarted
|
|
164
|
+
? "Daemon auto-started."
|
|
165
|
+
: "Daemon already running."
|
|
166
|
+
: `Daemon could not be started — run: wechat-claude daemon (or node ${DAEMON_PATH})`;
|
|
167
|
+
return {
|
|
168
|
+
content: [
|
|
169
|
+
{
|
|
170
|
+
type: "text",
|
|
171
|
+
text: `Login successful! Session: "${sessionName.value}".\n\n${daemonNote}`,
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
if (status.status === "expired") {
|
|
177
|
+
return {
|
|
178
|
+
content: [
|
|
179
|
+
{ type: "text", text: "QR code expired. Call wechat_login again." },
|
|
180
|
+
],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
content: [
|
|
185
|
+
{
|
|
186
|
+
type: "text",
|
|
187
|
+
text: `Status: ${status.status}. ${status.status === "scaned" ? "Scanned, awaiting confirmation..." : "Waiting for scan..."} Keep polling.`,
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
return {
|
|
194
|
+
content: [
|
|
195
|
+
{
|
|
196
|
+
type: "text",
|
|
197
|
+
text: `Poll failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
isError: true,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
server.tool("wechat_get_messages", "Get unread WeChat messages routed to this session by the daemon.", {}, async () => {
|
|
205
|
+
touchActive();
|
|
206
|
+
touchHeartbeat(sessionId);
|
|
207
|
+
const msgs = readInbox();
|
|
208
|
+
if (msgs.length === 0) {
|
|
209
|
+
return { content: [{ type: "text", text: "No new messages." }] };
|
|
210
|
+
}
|
|
211
|
+
for (const m of msgs) {
|
|
212
|
+
if (m.contextToken && m.fromUserId) {
|
|
213
|
+
client.trackContextToken(m.fromUserId, m.contextToken);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const formatted = msgs.map((m) => {
|
|
217
|
+
const time = new Date(m.timestamp).toLocaleString("zh-CN");
|
|
218
|
+
return `[${time}] ${m.fromUserId}:\n${m.text}`;
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
content: [
|
|
222
|
+
{
|
|
223
|
+
type: "text",
|
|
224
|
+
text: `${msgs.length} new message(s):\n\n${formatted.join("\n\n---\n\n")}`,
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
server.tool("wechat_send_text", "Send a text message to a WeChat user.", {
|
|
230
|
+
to_user_id: z.string().describe("User ID (e.g. 'xxx@im.wechat')"),
|
|
231
|
+
text: z.string().describe("Text message to send"),
|
|
232
|
+
}, async ({ to_user_id, text }) => {
|
|
233
|
+
if (!client.isLoggedIn) {
|
|
234
|
+
return {
|
|
235
|
+
content: [{ type: "text", text: "Not logged in." }],
|
|
236
|
+
isError: true,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
clearTyping(to_user_id);
|
|
241
|
+
await client.sendText(to_user_id, text);
|
|
242
|
+
await client.sendTyping(to_user_id, false);
|
|
243
|
+
return {
|
|
244
|
+
content: [
|
|
245
|
+
{ type: "text", text: `Sent to ${to_user_id} (${text.length} chars).` },
|
|
246
|
+
],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
return {
|
|
251
|
+
content: [
|
|
252
|
+
{
|
|
253
|
+
type: "text",
|
|
254
|
+
text: `Send failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
isError: true,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
server.tool("wechat_send_image", "Send an image file to a WeChat user.", {
|
|
262
|
+
to_user_id: z.string().describe("User ID (e.g. 'xxx@im.wechat')"),
|
|
263
|
+
file_path: z.string().describe("Absolute path to the image file"),
|
|
264
|
+
caption: z.string().optional().describe("Optional text caption to send with the image"),
|
|
265
|
+
}, async ({ to_user_id, file_path, caption }) => {
|
|
266
|
+
if (!client.isLoggedIn) {
|
|
267
|
+
return {
|
|
268
|
+
content: [{ type: "text", text: "Not logged in." }],
|
|
269
|
+
isError: true,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
const fs = await import("node:fs");
|
|
274
|
+
if (!fs.existsSync(file_path)) {
|
|
275
|
+
return {
|
|
276
|
+
content: [{ type: "text", text: `File not found: ${file_path}` }],
|
|
277
|
+
isError: true,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
clearTyping(to_user_id);
|
|
281
|
+
await client.sendImage(to_user_id, file_path, caption);
|
|
282
|
+
await client.sendTyping(to_user_id, false);
|
|
283
|
+
return {
|
|
284
|
+
content: [
|
|
285
|
+
{ type: "text", text: `Image sent to ${to_user_id}.` },
|
|
286
|
+
],
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
return {
|
|
291
|
+
content: [
|
|
292
|
+
{
|
|
293
|
+
type: "text",
|
|
294
|
+
text: `Send image failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
isError: true,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
server.tool("wechat_set_session_name", "Set a name for this session for WeChat routing ('/s <name> <msg>').", { name: z.string().describe("Session name (e.g. 'backend', 'review')") }, async ({ name }) => {
|
|
302
|
+
sessionName.value = name;
|
|
303
|
+
register();
|
|
304
|
+
return {
|
|
305
|
+
content: [
|
|
306
|
+
{ type: "text", text: `Session name: "${name}". Route: /s ${name} <msg>` },
|
|
307
|
+
],
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
server.tool("wechat_status", "Check WeChat connection, daemon status, and active sessions. Call at session start to see if WeChat monitoring is available.", {}, async () => {
|
|
311
|
+
const daemon = await ensureDaemonRunning();
|
|
312
|
+
const sessions = listSessions();
|
|
313
|
+
const inboxCount = peekInbox();
|
|
314
|
+
const monitoring = isMonitoring(sessionId);
|
|
315
|
+
const lines = [
|
|
316
|
+
`Logged in: ${client.isLoggedIn}`,
|
|
317
|
+
`Daemon running: ${daemon.running}${daemon.autoStarted ? " (auto-started just now)" : ""}`,
|
|
318
|
+
`Session: ${sessionName.value} (id: ${sessionId})`,
|
|
319
|
+
`Inbox: ${inboxCount} message(s)`,
|
|
320
|
+
`Watcher: ${monitoring ? "active — this session is monitoring messages" : "NOT active — messages routed here will sit unread"}`,
|
|
321
|
+
`Active sessions (${sessions.length}):`,
|
|
322
|
+
...sessions.map((s) => {
|
|
323
|
+
const active = Date.now() - s.lastActive < 120_000 ? "●" : "○";
|
|
324
|
+
const mon = isMonitoring(s.id) ? " [monitoring]" : "";
|
|
325
|
+
return ` ${active} ${s.name} (pid: ${s.pid})${mon}`;
|
|
326
|
+
}),
|
|
327
|
+
];
|
|
328
|
+
if (isLoginExpired()) {
|
|
329
|
+
lines.push("", "WeChat login has EXPIRED. Call wechat_login to re-authenticate (scan QR code).");
|
|
330
|
+
}
|
|
331
|
+
else if (!client.isLoggedIn) {
|
|
332
|
+
lines.push("", "Not logged in. Call wechat_login to authenticate.");
|
|
333
|
+
}
|
|
334
|
+
if (!daemon.running && client.isLoggedIn) {
|
|
335
|
+
lines.push("", `Daemon is NOT running and auto-start failed. Check ${DAEMON_LOG_FILE}, or start manually: wechat-claude daemon (or node ${DAEMON_PATH} if the bin is not on PATH)`);
|
|
336
|
+
}
|
|
337
|
+
if (!monitoring) {
|
|
338
|
+
lines.push("", `To monitor messages in this session, start a persistent Monitor with: node ${WATCHER_PATH} ${sessionId}`);
|
|
339
|
+
}
|
|
340
|
+
if (client.isLoggedIn && daemon.running && monitoring) {
|
|
341
|
+
lines.push("", "WeChat is fully operational. Messages are being monitored.");
|
|
342
|
+
}
|
|
343
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
344
|
+
});
|
|
345
|
+
server.tool("wechat_logout", "Disconnect from WeChat and clear session.", {}, async () => {
|
|
346
|
+
client.logout();
|
|
347
|
+
return {
|
|
348
|
+
content: [{ type: "text", text: "Logged out. Session cleared." }],
|
|
349
|
+
};
|
|
350
|
+
});
|
|
351
|
+
async function main() {
|
|
352
|
+
ensureDirs();
|
|
353
|
+
register();
|
|
354
|
+
process.on("exit", unregister);
|
|
355
|
+
client.tryRestoreSession();
|
|
356
|
+
process.stderr.write(`[wechat-claude] MCP server started. Session: "${sessionName.value}", logged in: ${client.isLoggedIn}\n`);
|
|
357
|
+
const transport = new StdioServerTransport();
|
|
358
|
+
await server.connect(transport);
|
|
359
|
+
}
|
|
360
|
+
main().catch((err) => {
|
|
361
|
+
process.stderr.write(`Fatal: ${err}\n`);
|
|
362
|
+
process.exit(1);
|
|
363
|
+
});
|
|
364
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function assignSessionNumbers(liveIds: string[]): Record<string, number>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const FILE = path.join(os.homedir(), ".claude", "wechat", "session-numbers.json");
|
|
5
|
+
function readRegistry() {
|
|
6
|
+
try {
|
|
7
|
+
const parsed = JSON.parse(fs.readFileSync(FILE, "utf-8"));
|
|
8
|
+
if (typeof parsed === "object" &&
|
|
9
|
+
parsed !== null &&
|
|
10
|
+
typeof parsed.next === "number" &&
|
|
11
|
+
typeof parsed.map === "object") {
|
|
12
|
+
return parsed;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
catch { }
|
|
16
|
+
return { next: 1, map: {} };
|
|
17
|
+
}
|
|
18
|
+
// Assign stable display numbers to sessions. A session keeps its number for
|
|
19
|
+
// its whole lifetime; numbers of closed sessions are retired (not reused), so
|
|
20
|
+
// "/s 3" always means the same session no matter what opened or closed in
|
|
21
|
+
// between. The counter resets to 1 once no sessions are left.
|
|
22
|
+
export function assignSessionNumbers(liveIds) {
|
|
23
|
+
const reg = readRegistry();
|
|
24
|
+
const map = {};
|
|
25
|
+
for (const [id, num] of Object.entries(reg.map)) {
|
|
26
|
+
if (liveIds.includes(id))
|
|
27
|
+
map[id] = num;
|
|
28
|
+
}
|
|
29
|
+
let next = Object.keys(map).length === 0 ? 1 : reg.next;
|
|
30
|
+
const newIds = liveIds
|
|
31
|
+
.filter((id) => !(id in map))
|
|
32
|
+
.sort((a, b) => Number(a) - Number(b));
|
|
33
|
+
for (const id of newIds) {
|
|
34
|
+
map[id] = next;
|
|
35
|
+
next += 1;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
fs.mkdirSync(path.dirname(FILE), { recursive: true });
|
|
39
|
+
fs.writeFileSync(FILE, JSON.stringify({ next, map }));
|
|
40
|
+
}
|
|
41
|
+
catch { }
|
|
42
|
+
return map;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=session-numbers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-numbers.js","sourceRoot":"","sources":["../src/session-numbers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CACpB,EAAE,CAAC,OAAO,EAAE,EACZ,SAAS,EACT,QAAQ,EACR,sBAAsB,CACvB,CAAC;AAIF,SAAS,YAAY;IACnB,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACnE,IACE,OAAO,MAAM,KAAK,QAAQ;YAC1B,MAAM,KAAK,IAAI;YACf,OAAQ,MAAmB,CAAC,IAAI,KAAK,QAAQ;YAC7C,OAAQ,MAAmB,CAAC,GAAG,KAAK,QAAQ,EAC5C,CAAC;YACD,OAAO,MAAkB,CAAC;QAC5B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;AAC9B,CAAC;AAED,4EAA4E;AAC5E,8EAA8E;AAC9E,0EAA0E;AAC1E,8DAA8D;AAC9D,MAAM,UAAU,oBAAoB,CAClC,OAAiB;IAEjB,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC;IAC3B,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAChD,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAAE,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;IAC1C,CAAC;IACD,IAAI,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IACxD,MAAM,MAAM,GAAG,OAAO;SACnB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC;SAC5B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QACxB,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;QACf,IAAI,IAAI,CAAC,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type SessionInfo = {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
cwd: string;
|
|
5
|
+
pid: number;
|
|
6
|
+
lastActive: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function detectSessionName(cwd: string): string;
|
|
9
|
+
export declare function writeSessionFile(info: SessionInfo): void;
|
|
10
|
+
export declare function listSessions(): SessionInfo[];
|
|
11
|
+
export declare function sortedSessions(): SessionInfo[];
|
|
12
|
+
export declare function getDefaultTarget(sessions: SessionInfo[]): SessionInfo | undefined;
|
|
13
|
+
export declare function findSession(selector: string): SessionInfo | undefined;
|
|
14
|
+
export declare function matchSessions(selector: string): SessionInfo[];
|
|
15
|
+
export declare function sessionLabel(s: SessionInfo, all: SessionInfo[]): string;
|