arceus-s 1.6.4 → 1.6.5
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/index.js +16 -0
- package/dist/cli/stop.d.ts +3 -0
- package/dist/cli/stop.js +122 -0
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -99,6 +99,7 @@ Commands:
|
|
|
99
99
|
analyze [path] Index a repository (full analysis)
|
|
100
100
|
index [path...] Register an existing .arc/ folder into the global registry
|
|
101
101
|
serve Start local HTTP server for web UI connection
|
|
102
|
+
stop Stop the local HTTP server on a port (default: 4747)
|
|
102
103
|
mcp Start MCP server (stdio) — serves all indexed repos
|
|
103
104
|
list List all indexed repositories
|
|
104
105
|
status Show index status for current repo
|
|
@@ -175,6 +176,14 @@ Options:
|
|
|
175
176
|
-p, --port <port> Port number (default: 4747)
|
|
176
177
|
--host <host> Bind address (default: 127.0.0.1)`);
|
|
177
178
|
break;
|
|
179
|
+
case 'stop':
|
|
180
|
+
console.log(`Usage: ${binName} stop [options]
|
|
181
|
+
|
|
182
|
+
Stop the local HTTP server on a port
|
|
183
|
+
|
|
184
|
+
Options:
|
|
185
|
+
-p, --port <port> Port number (default: 4747)`);
|
|
186
|
+
break;
|
|
178
187
|
case 'clean':
|
|
179
188
|
console.log(`Usage: ${binName} clean [options]
|
|
180
189
|
|
|
@@ -369,6 +378,13 @@ async function runCLI() {
|
|
|
369
378
|
await serveCommand(options);
|
|
370
379
|
break;
|
|
371
380
|
}
|
|
381
|
+
case 'stop': {
|
|
382
|
+
if (options.port === undefined)
|
|
383
|
+
options.port = '4747';
|
|
384
|
+
const { stopCommand } = await import('./stop.js');
|
|
385
|
+
await stopCommand(options);
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
372
388
|
case 'mcp': {
|
|
373
389
|
const { mcpCommand } = await import('./mcp.js');
|
|
374
390
|
await mcpCommand();
|
package/dist/cli/stop.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { logger } from '../core/logger.js';
|
|
4
|
+
const execAsync = promisify(exec);
|
|
5
|
+
export const stopCommand = async (options) => {
|
|
6
|
+
const port = Number(options?.port ?? 4747);
|
|
7
|
+
if (isNaN(port) || port <= 0 || port > 65535) {
|
|
8
|
+
console.error(`Error: Invalid port number: ${options?.port}`);
|
|
9
|
+
process.exitCode = 1;
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
console.log(`Stopping process on port ${port}...`);
|
|
13
|
+
try {
|
|
14
|
+
const pids = await findPidsOnPort(port);
|
|
15
|
+
if (pids.size === 0) {
|
|
16
|
+
console.log(`No active process found using port ${port}.`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const killedPids = [];
|
|
20
|
+
const failedPids = [];
|
|
21
|
+
for (const pid of pids) {
|
|
22
|
+
if (pid === process.pid) {
|
|
23
|
+
// Don't kill ourselves
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
await killPid(pid);
|
|
28
|
+
killedPids.push(pid);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
logger.error({ err, pid }, 'Failed to kill process');
|
|
32
|
+
failedPids.push(pid);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (killedPids.length > 0) {
|
|
36
|
+
console.log(`Successfully stopped process(es) on port ${port} (PID: ${killedPids.join(', ')}).`);
|
|
37
|
+
}
|
|
38
|
+
if (failedPids.length > 0) {
|
|
39
|
+
console.error(`Failed to stop process(es) on port ${port} (PID: ${failedPids.join(', ')}).`);
|
|
40
|
+
process.exitCode = 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
console.error(`Error while scanning port ${port}:`, err.message || err);
|
|
45
|
+
process.exitCode = 1;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
async function findPidsOnPort(port) {
|
|
49
|
+
const pids = new Set();
|
|
50
|
+
if (process.platform === 'win32') {
|
|
51
|
+
try {
|
|
52
|
+
const { stdout } = await execAsync('netstat -ano');
|
|
53
|
+
const lines = stdout.split('\n');
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
const trimmed = line.trim();
|
|
56
|
+
if (!trimmed)
|
|
57
|
+
continue;
|
|
58
|
+
const parts = trimmed.split(/\s+/);
|
|
59
|
+
if (parts.length < 4)
|
|
60
|
+
continue;
|
|
61
|
+
// Local address is the second column (index 1)
|
|
62
|
+
const localAddress = parts[1];
|
|
63
|
+
if (localAddress &&
|
|
64
|
+
(localAddress.endsWith(`:${port}`) || localAddress.endsWith(`]:${port}`))) {
|
|
65
|
+
const pidStr = parts[parts.length - 1];
|
|
66
|
+
const pid = parseInt(pidStr, 10);
|
|
67
|
+
if (!isNaN(pid) && pid > 0) {
|
|
68
|
+
pids.add(pid);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
logger.debug({ err }, 'netstat execution failed');
|
|
75
|
+
throw new Error(`Failed to list connections via netstat: ${err.message}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// macOS / Linux
|
|
80
|
+
try {
|
|
81
|
+
const { stdout } = await execAsync(`lsof -t -i :${port}`);
|
|
82
|
+
const lines = stdout.trim().split('\n');
|
|
83
|
+
for (const line of lines) {
|
|
84
|
+
const pid = parseInt(line.trim(), 10);
|
|
85
|
+
if (!isNaN(pid) && pid > 0) {
|
|
86
|
+
pids.add(pid);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
// lsof returns exit code 1 if no matches are found, which is not a real failure
|
|
92
|
+
if (err.code !== 1) {
|
|
93
|
+
// Try fallback to fuser
|
|
94
|
+
try {
|
|
95
|
+
const { stdout } = await execAsync(`fuser ${port}/tcp`);
|
|
96
|
+
const lines = stdout.trim().split(/\s+/);
|
|
97
|
+
for (const line of lines) {
|
|
98
|
+
const pid = parseInt(line.trim(), 10);
|
|
99
|
+
if (!isNaN(pid) && pid > 0) {
|
|
100
|
+
pids.add(pid);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (fuserErr) {
|
|
105
|
+
logger.debug({ fuserErr }, 'fuser execution failed');
|
|
106
|
+
if (fuserErr.code !== 1) {
|
|
107
|
+
throw new Error(`Failed to list connections via lsof/fuser: ${err.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return pids;
|
|
114
|
+
}
|
|
115
|
+
async function killPid(pid) {
|
|
116
|
+
if (process.platform === 'win32') {
|
|
117
|
+
await execAsync(`taskkill /F /PID ${pid}`);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
await execAsync(`kill -9 ${pid}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arceus-s",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.5",
|
|
4
4
|
"description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
|
|
5
5
|
"author": "Chandan Kumar Behera",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
@@ -119,4 +119,4 @@
|
|
|
119
119
|
"engines": {
|
|
120
120
|
"node": ">=22.0.0"
|
|
121
121
|
}
|
|
122
|
-
}
|
|
122
|
+
}
|