@adapt-toolkit/a2adapt 0.2.0 → 0.3.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/dist/cli.js ADDED
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
3
+
4
+ // src/cli.ts
5
+ import { spawn } from "node:child_process";
6
+ import { connect } from "node:net";
7
+ import { homedir } from "node:os";
8
+ import { resolve, join, dirname } from "node:path";
9
+ import { fileURLToPath, pathToFileURL } from "node:url";
10
+ import * as fs from "node:fs";
11
+ var STATE_DIR = resolve(process.env.A2ADAPT_STATE_DIR ?? resolve(homedir(), ".a2adapt"));
12
+ var PORT = parseInt(process.env.A2ADAPT_PORT ?? "3030", 10);
13
+ var BROKER_URL = process.env.A2ADAPT_BROKER_URL ?? "wss://a2adapt.adaptframework.solutions/broker";
14
+ var PID_PATH = join(STATE_DIR, "daemon.pid");
15
+ var LOG_PATH = join(STATE_DIR, "daemon.log");
16
+ var SELF = fileURLToPath(import.meta.url);
17
+ var out = (...p) => process.stdout.write(`${p.join(" ")}
18
+ `);
19
+ var err = (...p) => process.stderr.write(`${p.join(" ")}
20
+ `);
21
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
22
+ function readPid() {
23
+ try {
24
+ const n = parseInt(fs.readFileSync(PID_PATH, "utf8").trim(), 10);
25
+ return Number.isFinite(n) ? n : null;
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+ function isAlive(pid) {
31
+ try {
32
+ process.kill(pid, 0);
33
+ return true;
34
+ } catch {
35
+ return false;
36
+ }
37
+ }
38
+ function runningPid() {
39
+ const pid = readPid();
40
+ if (pid && isAlive(pid)) return pid;
41
+ if (pid) {
42
+ try {
43
+ fs.rmSync(PID_PATH, { force: true });
44
+ } catch {
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+ function portOpen(port, timeoutMs = 1e3) {
50
+ return new Promise((res) => {
51
+ const sock = connect({ host: "127.0.0.1", port });
52
+ const done = (ok) => {
53
+ sock.destroy();
54
+ res(ok);
55
+ };
56
+ sock.setTimeout(timeoutMs);
57
+ sock.once("connect", () => done(true));
58
+ sock.once("timeout", () => done(false));
59
+ sock.once("error", () => done(false));
60
+ });
61
+ }
62
+ async function waitForPort(port, totalMs = 3e4) {
63
+ const deadline = Date.now() + totalMs;
64
+ while (Date.now() < deadline) {
65
+ if (await portOpen(port)) return true;
66
+ await sleep(400);
67
+ }
68
+ return false;
69
+ }
70
+ async function cmdStart() {
71
+ const existing = runningPid();
72
+ if (existing) {
73
+ out(`a2adapt-mcp is already running (pid ${existing}, port ${PORT}).`);
74
+ return;
75
+ }
76
+ fs.mkdirSync(STATE_DIR, { recursive: true });
77
+ const logFd = fs.openSync(LOG_PATH, "a");
78
+ const child = spawn(process.execPath, [SELF, "serve"], {
79
+ detached: true,
80
+ stdio: ["ignore", logFd, logFd],
81
+ env: {
82
+ ...process.env,
83
+ A2ADAPT_TRANSPORT: "http",
84
+ A2ADAPT_PORT: String(PORT),
85
+ A2ADAPT_BROKER_URL: BROKER_URL,
86
+ A2ADAPT_STATE_DIR: STATE_DIR
87
+ }
88
+ });
89
+ child.unref();
90
+ if (!child.pid) {
91
+ err("failed to spawn the daemon.");
92
+ process.exit(1);
93
+ }
94
+ fs.writeFileSync(PID_PATH, String(child.pid));
95
+ out(`starting a2adapt-mcp (pid ${child.pid})\u2026`);
96
+ const ready = await waitForPort(PORT);
97
+ if (ready) {
98
+ out(`a2adapt-mcp is up on http://localhost:${PORT}/mcp`);
99
+ out(` broker: ${BROKER_URL}`);
100
+ out(` state: ${STATE_DIR}`);
101
+ out(` logs: ${LOG_PATH}`);
102
+ } else {
103
+ err(`daemon started (pid ${child.pid}) but port ${PORT} did not open within 30s \u2014 check ${LOG_PATH}.`);
104
+ process.exit(1);
105
+ }
106
+ }
107
+ async function cmdStop() {
108
+ const pid = runningPid();
109
+ if (!pid) {
110
+ out("a2adapt-mcp is not running.");
111
+ return;
112
+ }
113
+ out(`stopping a2adapt-mcp (pid ${pid})\u2026`);
114
+ try {
115
+ process.kill(pid, "SIGTERM");
116
+ } catch (e) {
117
+ err(`failed to signal pid ${pid}: ${String(e)}`);
118
+ }
119
+ for (let i = 0; i < 25 && isAlive(pid); i++) await sleep(200);
120
+ if (isAlive(pid)) {
121
+ err(`pid ${pid} did not exit; sending SIGKILL.`);
122
+ try {
123
+ process.kill(pid, "SIGKILL");
124
+ } catch {
125
+ }
126
+ }
127
+ try {
128
+ fs.rmSync(PID_PATH, { force: true });
129
+ } catch {
130
+ }
131
+ out("stopped.");
132
+ }
133
+ async function cmdStatus() {
134
+ const pid = runningPid();
135
+ if (!pid) {
136
+ out("a2adapt-mcp: stopped");
137
+ process.exitCode = 1;
138
+ return;
139
+ }
140
+ const up = await portOpen(PORT);
141
+ out("a2adapt-mcp: running");
142
+ out(` pid: ${pid}`);
143
+ out(` url: http://localhost:${PORT}/mcp ${up ? "(reachable)" : "(port not answering!)"}`);
144
+ out(` broker: ${BROKER_URL}`);
145
+ out(` state: ${STATE_DIR}`);
146
+ out(` logs: ${LOG_PATH}`);
147
+ }
148
+ function usage() {
149
+ out("a2adapt-mcp \u2014 daemon for the a2adapt MCP server");
150
+ out("");
151
+ out("Usage: a2adapt-mcp <command>");
152
+ out(" start start the daemon in the background");
153
+ out(" stop stop the running daemon");
154
+ out(" restart stop then start");
155
+ out(" status show whether the daemon is running");
156
+ out(" serve run in the foreground (used by start; handy for debugging)");
157
+ out("");
158
+ out("Config (env): A2ADAPT_BROKER_URL, A2ADAPT_PORT (3030), A2ADAPT_STATE_DIR (~/.a2adapt)");
159
+ }
160
+ async function main() {
161
+ const cmd = process.argv[2] ?? "help";
162
+ switch (cmd) {
163
+ case "serve":
164
+ case "run":
165
+ if (!process.env.A2ADAPT_TRANSPORT) process.env.A2ADAPT_TRANSPORT = "http";
166
+ await import(pathToFileURL(join(dirname(SELF), "index.js")).href);
167
+ break;
168
+ case "start":
169
+ await cmdStart();
170
+ break;
171
+ case "stop":
172
+ await cmdStop();
173
+ break;
174
+ case "restart":
175
+ await cmdStop();
176
+ await cmdStart();
177
+ break;
178
+ case "status":
179
+ await cmdStatus();
180
+ break;
181
+ case "help":
182
+ case "--help":
183
+ case "-h":
184
+ usage();
185
+ break;
186
+ default:
187
+ err(`unknown command: ${cmd}
188
+ `);
189
+ usage();
190
+ process.exit(1);
191
+ }
192
+ }
193
+ main().catch((e) => {
194
+ err(`a2adapt-mcp error: ${e?.stack ?? e}`);
195
+ process.exit(1);
196
+ });