fellow-agents 0.0.11 → 0.0.14
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 +18 -2
- package/dist/commands/clean.js +72 -0
- package/dist/commands/uninstall.js +99 -0
- package/dist/lib/services.js +13 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
const args = process.argv.slice(2);
|
|
3
3
|
// Default to "start" if no command given or first arg is a flag
|
|
4
4
|
const command = args[0] === "stop" ? "stop"
|
|
5
|
-
:
|
|
6
|
-
: "
|
|
5
|
+
: args[0] === "clean" ? "clean"
|
|
6
|
+
: args[0] === "uninstall" ? "uninstall"
|
|
7
|
+
: (args[0] === "--help" || args[0] === "-h") ? "help"
|
|
8
|
+
: "start";
|
|
7
9
|
function getFlag(name, fallback) {
|
|
8
10
|
const idx = args.indexOf(name);
|
|
9
11
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : fallback;
|
|
@@ -25,12 +27,25 @@ else if (command === "stop") {
|
|
|
25
27
|
const { stop } = await import("./commands/stop.js");
|
|
26
28
|
stop();
|
|
27
29
|
}
|
|
30
|
+
else if (command === "clean") {
|
|
31
|
+
const { clean } = await import("./commands/clean.js");
|
|
32
|
+
clean();
|
|
33
|
+
}
|
|
34
|
+
else if (command === "uninstall") {
|
|
35
|
+
const { uninstall } = await import("./commands/uninstall.js");
|
|
36
|
+
uninstall({
|
|
37
|
+
dir: getFlag("--dir", process.cwd()),
|
|
38
|
+
yes: hasFlag("--yes"),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
28
41
|
else {
|
|
29
42
|
console.log(`fellow-agents — multi-agent system for Claude Code
|
|
30
43
|
|
|
31
44
|
Usage:
|
|
32
45
|
fellow-agents [options] Start services (default)
|
|
33
46
|
fellow-agents stop Stop all running services
|
|
47
|
+
fellow-agents clean Wipe cached binaries + pty-win, preserve logs
|
|
48
|
+
fellow-agents uninstall [--yes] Remove all fellow-agents state (data dir + workspaces)
|
|
34
49
|
|
|
35
50
|
Options:
|
|
36
51
|
--port <number> pty-win port (default: 3700)
|
|
@@ -38,6 +53,7 @@ Options:
|
|
|
38
53
|
--dir <path> Working directory (default: current)
|
|
39
54
|
--no-browser Don't open browser
|
|
40
55
|
--update Force re-download binaries
|
|
56
|
+
--yes Skip confirmation prompt (uninstall only)
|
|
41
57
|
|
|
42
58
|
-h, --help Show this help`);
|
|
43
59
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { existsSync, rmSync, statSync, readdirSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { dataDir, binDir, ptyWinDir, pidDir } from "../lib/paths.js";
|
|
4
|
+
import { stopAll } from "../lib/services.js";
|
|
5
|
+
function dirSize(path) {
|
|
6
|
+
if (!existsSync(path))
|
|
7
|
+
return 0;
|
|
8
|
+
let total = 0;
|
|
9
|
+
const stack = [path];
|
|
10
|
+
while (stack.length > 0) {
|
|
11
|
+
const current = stack.pop();
|
|
12
|
+
try {
|
|
13
|
+
const stat = statSync(current);
|
|
14
|
+
if (stat.isDirectory()) {
|
|
15
|
+
for (const entry of readdirSync(current))
|
|
16
|
+
stack.push(join(current, entry));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
total += stat.size;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch { }
|
|
23
|
+
}
|
|
24
|
+
return total;
|
|
25
|
+
}
|
|
26
|
+
function formatBytes(bytes) {
|
|
27
|
+
if (bytes < 1024)
|
|
28
|
+
return `${bytes} B`;
|
|
29
|
+
if (bytes < 1024 * 1024)
|
|
30
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
31
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
32
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
33
|
+
return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
34
|
+
}
|
|
35
|
+
export function clean() {
|
|
36
|
+
console.log("");
|
|
37
|
+
console.log(" Cleaning fellow-agents state...");
|
|
38
|
+
stopAll();
|
|
39
|
+
// Wipe these — logs/ is preserved for postmortem
|
|
40
|
+
const targets = [
|
|
41
|
+
{ label: "bin", path: binDir },
|
|
42
|
+
{ label: "pty-win", path: ptyWinDir },
|
|
43
|
+
{ label: "pid", path: pidDir },
|
|
44
|
+
];
|
|
45
|
+
let totalFreed = 0;
|
|
46
|
+
for (const t of targets) {
|
|
47
|
+
if (existsSync(t.path)) {
|
|
48
|
+
const size = dirSize(t.path);
|
|
49
|
+
try {
|
|
50
|
+
rmSync(t.path, { recursive: true, force: true });
|
|
51
|
+
console.log(` Removed ${t.label}/ (${formatBytes(size)})`);
|
|
52
|
+
totalFreed += size;
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
console.error(` Failed to remove ${t.label}/: ${err.message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Reset version stamp (separate from bin/ since it's nested but worth calling out)
|
|
60
|
+
const versionPath = join(dataDir, "bin", ".version");
|
|
61
|
+
if (existsSync(versionPath)) {
|
|
62
|
+
try {
|
|
63
|
+
rmSync(versionPath);
|
|
64
|
+
}
|
|
65
|
+
catch { }
|
|
66
|
+
}
|
|
67
|
+
console.log("");
|
|
68
|
+
console.log(` Cleaned ${formatBytes(totalFreed)} from ${dataDir}`);
|
|
69
|
+
console.log(` Logs preserved at ${join(dataDir, "logs")}`);
|
|
70
|
+
console.log(` Run 'fellow-agents' to reinstall.`);
|
|
71
|
+
console.log("");
|
|
72
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { existsSync, rmSync, statSync, readdirSync } from "fs";
|
|
2
|
+
import { join, resolve } from "path";
|
|
3
|
+
import { dataDir } from "../lib/paths.js";
|
|
4
|
+
import { stopAll } from "../lib/services.js";
|
|
5
|
+
function dirSize(path) {
|
|
6
|
+
if (!existsSync(path))
|
|
7
|
+
return 0;
|
|
8
|
+
let total = 0;
|
|
9
|
+
const stack = [path];
|
|
10
|
+
while (stack.length > 0) {
|
|
11
|
+
const current = stack.pop();
|
|
12
|
+
try {
|
|
13
|
+
const stat = statSync(current);
|
|
14
|
+
if (stat.isDirectory()) {
|
|
15
|
+
for (const entry of readdirSync(current))
|
|
16
|
+
stack.push(join(current, entry));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
total += stat.size;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch { }
|
|
23
|
+
}
|
|
24
|
+
return total;
|
|
25
|
+
}
|
|
26
|
+
function formatBytes(bytes) {
|
|
27
|
+
if (bytes < 1024)
|
|
28
|
+
return `${bytes} B`;
|
|
29
|
+
if (bytes < 1024 * 1024)
|
|
30
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
31
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
32
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
33
|
+
return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
34
|
+
}
|
|
35
|
+
export function uninstall(opts) {
|
|
36
|
+
const workDir = resolve(opts.dir);
|
|
37
|
+
// Two possible workspace locations — start.ts uses either depending on whether cwd has workspaces/
|
|
38
|
+
const wsCandidates = [
|
|
39
|
+
join(workDir, "workspaces"),
|
|
40
|
+
join(workDir, "fellow-agents", "workspaces"),
|
|
41
|
+
];
|
|
42
|
+
const targets = [];
|
|
43
|
+
if (existsSync(dataDir)) {
|
|
44
|
+
targets.push({ label: "Data directory", path: dataDir, size: dirSize(dataDir) });
|
|
45
|
+
}
|
|
46
|
+
for (const ws of wsCandidates) {
|
|
47
|
+
if (existsSync(ws)) {
|
|
48
|
+
targets.push({ label: "Workspaces", path: ws, size: dirSize(ws) });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
console.log("");
|
|
52
|
+
console.log(" fellow-agents uninstall");
|
|
53
|
+
console.log(" =======================");
|
|
54
|
+
console.log("");
|
|
55
|
+
if (targets.length === 0) {
|
|
56
|
+
console.log(" Nothing to remove — no fellow-agents state found.");
|
|
57
|
+
console.log("");
|
|
58
|
+
console.log(" To uninstall the npm package itself, run:");
|
|
59
|
+
console.log(" npm uninstall -g fellow-agents");
|
|
60
|
+
console.log("");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const totalSize = targets.reduce((sum, t) => sum + t.size, 0);
|
|
64
|
+
console.log(" The following will be permanently removed:");
|
|
65
|
+
console.log("");
|
|
66
|
+
for (const t of targets) {
|
|
67
|
+
console.log(` ${t.path} (${formatBytes(t.size)})`);
|
|
68
|
+
}
|
|
69
|
+
console.log("");
|
|
70
|
+
console.log(` Total: ${formatBytes(totalSize)}`);
|
|
71
|
+
console.log("");
|
|
72
|
+
if (!opts.yes) {
|
|
73
|
+
console.log(" This is a dry run. To proceed, add --yes:");
|
|
74
|
+
console.log(" fellow-agents uninstall --yes");
|
|
75
|
+
console.log("");
|
|
76
|
+
console.log(" Tip: pass --dir <path> if your workspaces are elsewhere.");
|
|
77
|
+
console.log("");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Actually do the uninstall
|
|
81
|
+
console.log(" Stopping services...");
|
|
82
|
+
stopAll();
|
|
83
|
+
console.log("");
|
|
84
|
+
for (const t of targets) {
|
|
85
|
+
try {
|
|
86
|
+
rmSync(t.path, { recursive: true, force: true });
|
|
87
|
+
console.log(` Removed ${t.path}`);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
console.error(` Failed to remove ${t.path}: ${err.message}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
console.log("");
|
|
94
|
+
console.log(" fellow-agents state removed.");
|
|
95
|
+
console.log("");
|
|
96
|
+
console.log(" To uninstall the npm package itself, run:");
|
|
97
|
+
console.log(" npm uninstall -g fellow-agents");
|
|
98
|
+
console.log("");
|
|
99
|
+
}
|
package/dist/lib/services.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from "child_process";
|
|
1
|
+
import { spawn, execSync } from "child_process";
|
|
2
2
|
import http from "http";
|
|
3
3
|
import https from "https";
|
|
4
4
|
import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync, openSync } from "fs";
|
|
@@ -78,12 +78,23 @@ export function startPtyWin(port, workspacesDir, emcomUrl, env) {
|
|
|
78
78
|
writePid("pty-win", proc.pid);
|
|
79
79
|
return proc.pid;
|
|
80
80
|
}
|
|
81
|
+
// On Windows, PyInstaller --onefile binaries (like emcom-server) run as a bootloader + child
|
|
82
|
+
// process. process.kill() only kills the bootloader; the child keeps the socket. taskkill /T
|
|
83
|
+
// walks the process tree so the child dies too.
|
|
84
|
+
function killTree(pid) {
|
|
85
|
+
if (process.platform === "win32") {
|
|
86
|
+
execSync(`taskkill /F /T /PID ${pid}`, { stdio: "ignore" });
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
process.kill(pid);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
81
92
|
export function stopAll() {
|
|
82
93
|
for (const name of ["emcom-server", "pty-win"]) {
|
|
83
94
|
const pid = readPid(name);
|
|
84
95
|
if (pid && isRunning(pid)) {
|
|
85
96
|
try {
|
|
86
|
-
|
|
97
|
+
killTree(pid);
|
|
87
98
|
console.log(` Stopped ${name} (pid ${pid})`);
|
|
88
99
|
}
|
|
89
100
|
catch {
|