@tempad-dev/mcp 0.3.4 → 0.3.6

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/dist/cli.mjs ADDED
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env node
2
+ import { a as SOCK_PATH, c as log, i as RUNTIME_DIR, n as LOCK_PATH, o as ensureDir, r as PACKAGE_VERSION } from "./shared-DisdM-7P.mjs";
3
+ import { spawn } from "node:child_process";
4
+ import { connect } from "node:net";
5
+ import { join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import lockfile from "proper-lockfile";
8
+
9
+ //#region src/cli.ts
10
+ let activeSocket = null;
11
+ let shuttingDown = false;
12
+ function closeActiveSocket() {
13
+ if (!activeSocket) return;
14
+ try {
15
+ activeSocket.end();
16
+ } catch {}
17
+ try {
18
+ activeSocket.destroy();
19
+ } catch {}
20
+ activeSocket = null;
21
+ }
22
+ function shutdownCli(reason) {
23
+ if (shuttingDown) return;
24
+ shuttingDown = true;
25
+ log.info(`${reason} Shutting down CLI.`);
26
+ closeActiveSocket();
27
+ process.exit(0);
28
+ }
29
+ process.on("SIGINT", () => shutdownCli("SIGINT received."));
30
+ process.on("SIGTERM", () => shutdownCli("SIGTERM received."));
31
+ const HUB_STARTUP_TIMEOUT = 5e3;
32
+ const CONNECT_RETRY_DELAY = 200;
33
+ const FAILED_RESTART_DELAY = 5e3;
34
+ const HUB_ENTRY = join(fileURLToPath(new URL(".", import.meta.url)), "hub.js");
35
+ ensureDir(RUNTIME_DIR);
36
+ function bridge(socket) {
37
+ return new Promise((resolve) => {
38
+ log.info("Bridge established with Hub. Forwarding I/O.");
39
+ activeSocket = socket;
40
+ const onStdinEnd = () => {
41
+ shutdownCli("Consumer stream ended.");
42
+ };
43
+ process.stdin.once("end", onStdinEnd);
44
+ const onSocketClose = () => {
45
+ log.warn("Connection to Hub lost. Attempting to reconnect...");
46
+ activeSocket = null;
47
+ process.stdin.removeListener("end", onStdinEnd);
48
+ process.stdin.unpipe(socket);
49
+ socket.unpipe(process.stdout);
50
+ socket.removeAllListeners();
51
+ resolve();
52
+ };
53
+ socket.once("close", onSocketClose);
54
+ socket.on("error", (err) => log.warn({ err }, "Socket error occurred."));
55
+ process.stdin.pipe(socket, { end: false }).pipe(process.stdout);
56
+ });
57
+ }
58
+ function connectHub() {
59
+ return new Promise((resolve, reject) => {
60
+ const socket = connect(SOCK_PATH);
61
+ socket.on("connect", () => {
62
+ socket.removeAllListeners("error");
63
+ resolve(socket);
64
+ });
65
+ socket.on("error", reject);
66
+ });
67
+ }
68
+ async function connectWithRetry(timeout) {
69
+ const startTime = Date.now();
70
+ let delay = CONNECT_RETRY_DELAY;
71
+ while (Date.now() - startTime < timeout) try {
72
+ return await connectHub();
73
+ } catch (err) {
74
+ if (err && typeof err === "object" && "code" in err && (err.code === "ENOENT" || err.code === "ECONNREFUSED")) {
75
+ const remainingTime = timeout - (Date.now() - startTime);
76
+ const waitTime = Math.min(delay, remainingTime);
77
+ if (waitTime <= 0) break;
78
+ await new Promise((r) => setTimeout(r, waitTime));
79
+ delay = Math.min(delay * 1.5, 1e3);
80
+ } else throw err;
81
+ }
82
+ throw new Error(`Failed to connect to Hub within ${timeout}ms.`);
83
+ }
84
+ function startHub() {
85
+ log.info("Spawning new Hub process...");
86
+ return spawn(process.execPath, [HUB_ENTRY], {
87
+ detached: true,
88
+ stdio: "ignore"
89
+ });
90
+ }
91
+ async function tryBecomeLeaderAndStartHub() {
92
+ let releaseLock = null;
93
+ try {
94
+ releaseLock = await lockfile.lock(LOCK_PATH, {
95
+ retries: {
96
+ retries: 5,
97
+ factor: 1.2,
98
+ minTimeout: 50
99
+ },
100
+ stale: 15e3
101
+ });
102
+ } catch {
103
+ log.info("Another process is starting the Hub. Waiting...");
104
+ return connectWithRetry(HUB_STARTUP_TIMEOUT);
105
+ }
106
+ log.info("Acquired lock. Starting Hub as the leader...");
107
+ let child = null;
108
+ try {
109
+ try {
110
+ return await connectHub();
111
+ } catch {
112
+ log.info("Hub not running. Proceeding to start it...");
113
+ }
114
+ child = startHub();
115
+ child.on("error", (err) => log.error({ err }, "Hub child process error."));
116
+ const socket = await connectWithRetry(HUB_STARTUP_TIMEOUT);
117
+ child.unref();
118
+ return socket;
119
+ } catch (err) {
120
+ log.error({ err }, "Failed to start or connect to the Hub.");
121
+ if (child && !child.killed) {
122
+ log.warn(`Killing stale Hub process (PID: ${child.pid})...`);
123
+ child.kill("SIGTERM");
124
+ }
125
+ throw err;
126
+ } finally {
127
+ if (releaseLock) await releaseLock();
128
+ }
129
+ }
130
+ async function main() {
131
+ log.info({ version: PACKAGE_VERSION }, "TemPad MCP Client starting...");
132
+ while (true) try {
133
+ await bridge(await connectHub().catch(() => {
134
+ log.info("Hub not running. Initiating startup sequence...");
135
+ return tryBecomeLeaderAndStartHub();
136
+ }));
137
+ log.info("Bridge disconnected. Restarting connection process...");
138
+ } catch (err) {
139
+ log.error({ err }, `Connection attempt failed. Retrying in ${FAILED_RESTART_DELAY / 1e3}s...`);
140
+ await new Promise((r) => setTimeout(r, FAILED_RESTART_DELAY));
141
+ }
142
+ }
143
+ main();
144
+
145
+ //#endregion
146
+ export { };
147
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":["activeSocket: Socket | null","err: unknown","releaseLock: (() => Promise<void>) | null","child: ChildProcess | null"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport type { ChildProcess } from 'node:child_process'\nimport type { Socket } from 'node:net'\n\nimport { spawn } from 'node:child_process'\nimport { connect } from 'node:net'\nimport { join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport lockfile from 'proper-lockfile'\n\nimport { PACKAGE_VERSION, log, LOCK_PATH, RUNTIME_DIR, SOCK_PATH, ensureDir } from './shared'\n\nlet activeSocket: Socket | null = null\nlet shuttingDown = false\n\nfunction closeActiveSocket() {\n if (!activeSocket) return\n try {\n activeSocket.end()\n } catch {\n // ignore\n }\n try {\n activeSocket.destroy()\n } catch {\n // ignore\n }\n activeSocket = null\n}\n\nfunction shutdownCli(reason: string) {\n if (shuttingDown) return\n shuttingDown = true\n log.info(`${reason} Shutting down CLI.`)\n closeActiveSocket()\n process.exit(0)\n}\n\nprocess.on('SIGINT', () => shutdownCli('SIGINT received.'))\nprocess.on('SIGTERM', () => shutdownCli('SIGTERM received.'))\n\nconst HUB_STARTUP_TIMEOUT = 5000\nconst CONNECT_RETRY_DELAY = 200\nconst FAILED_RESTART_DELAY = 5000\nconst HERE = fileURLToPath(new URL('.', import.meta.url))\nconst HUB_ENTRY = join(HERE, 'hub.js')\n\nensureDir(RUNTIME_DIR)\n\nfunction bridge(socket: Socket): Promise<void> {\n return new Promise((resolve) => {\n log.info('Bridge established with Hub. Forwarding I/O.')\n activeSocket = socket\n\n const onStdinEnd = () => {\n shutdownCli('Consumer stream ended.')\n }\n process.stdin.once('end', onStdinEnd)\n\n const onSocketClose = () => {\n log.warn('Connection to Hub lost. Attempting to reconnect...')\n activeSocket = null\n process.stdin.removeListener('end', onStdinEnd)\n process.stdin.unpipe(socket)\n socket.unpipe(process.stdout)\n socket.removeAllListeners()\n resolve()\n }\n socket.once('close', onSocketClose)\n socket.on('error', (err) => log.warn({ err }, 'Socket error occurred.'))\n\n // The `{ end: false }` option prevents stdin from closing the socket.\n process.stdin.pipe(socket, { end: false }).pipe(process.stdout)\n })\n}\n\nfunction connectHub(): Promise<Socket> {\n return new Promise((resolve, reject) => {\n const socket = connect(SOCK_PATH)\n socket.on('connect', () => {\n socket.removeAllListeners('error')\n resolve(socket)\n })\n socket.on('error', reject)\n })\n}\n\nasync function connectWithRetry(timeout: number): Promise<Socket> {\n const startTime = Date.now()\n let delay = CONNECT_RETRY_DELAY\n while (Date.now() - startTime < timeout) {\n try {\n return await connectHub()\n } catch (err: unknown) {\n if (\n err &&\n typeof err === 'object' &&\n 'code' in err &&\n (err.code === 'ENOENT' || err.code === 'ECONNREFUSED')\n ) {\n const remainingTime = timeout - (Date.now() - startTime)\n const waitTime = Math.min(delay, remainingTime)\n if (waitTime <= 0) break\n await new Promise((r) => setTimeout(r, waitTime))\n delay = Math.min(delay * 1.5, 1000)\n } else {\n throw err\n }\n }\n }\n throw new Error(`Failed to connect to Hub within ${timeout}ms.`)\n}\n\nfunction startHub(): ChildProcess {\n log.info('Spawning new Hub process...')\n return spawn(process.execPath, [HUB_ENTRY], {\n detached: true,\n stdio: 'ignore'\n })\n}\n\nasync function tryBecomeLeaderAndStartHub(): Promise<Socket> {\n let releaseLock: (() => Promise<void>) | null = null\n try {\n releaseLock = await lockfile.lock(LOCK_PATH, {\n retries: { retries: 5, factor: 1.2, minTimeout: 50 },\n stale: 15000\n })\n } catch {\n log.info('Another process is starting the Hub. Waiting...')\n return connectWithRetry(HUB_STARTUP_TIMEOUT)\n }\n\n log.info('Acquired lock. Starting Hub as the leader...')\n let child: ChildProcess | null = null\n try {\n try {\n return await connectHub()\n } catch {\n // If the Hub is not running, we proceed to start it.\n log.info('Hub not running. Proceeding to start it...')\n }\n child = startHub()\n child.on('error', (err) => log.error({ err }, 'Hub child process error.'))\n const socket = await connectWithRetry(HUB_STARTUP_TIMEOUT)\n child.unref()\n return socket\n } catch (err: unknown) {\n log.error({ err }, 'Failed to start or connect to the Hub.')\n if (child && !child.killed) {\n log.warn(`Killing stale Hub process (PID: ${child.pid})...`)\n child.kill('SIGTERM')\n }\n throw err\n } finally {\n if (releaseLock) await releaseLock()\n }\n}\n\nasync function main() {\n log.info({ version: PACKAGE_VERSION }, 'TemPad MCP Client starting...')\n\n while (true) {\n try {\n const socket = await connectHub().catch(() => {\n log.info('Hub not running. Initiating startup sequence...')\n return tryBecomeLeaderAndStartHub()\n })\n await bridge(socket)\n log.info('Bridge disconnected. Restarting connection process...')\n } catch (err: unknown) {\n log.error(\n { err },\n `Connection attempt failed. Retrying in ${FAILED_RESTART_DELAY / 1000}s...`\n )\n await new Promise((r) => setTimeout(r, FAILED_RESTART_DELAY))\n }\n }\n}\n\nmain()\n"],"mappings":"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"}