@tomagranate/corsa 1.0.0 → 1.0.2
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/README.md +6 -6
- package/bin/corsa +111 -0
- package/package.json +1 -1
- package/bin/corsa-linux-x64 +0 -0
- package/bin/toolui +0 -62
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# corsa
|
|
2
2
|
|
|
3
3
|
A Terminal User Interface (TUI) for managing multiple local development processes. View real-time logs, monitor status, and control all your dev servers from a single dashboard.
|
|
4
4
|
|
|
5
5
|
Built with [OpenTUI](https://github.com/anomalyco/opentui).
|
|
6
6
|
|
|
7
|
-
## Why
|
|
7
|
+
## Why corsa?
|
|
8
8
|
|
|
9
|
-
When working on a full-stack project, you often need to run multiple processes simultaneously—a frontend dev server, a backend API, database containers, workers, etc.
|
|
9
|
+
When working on a full-stack project, you often need to run multiple processes simultaneously—a frontend dev server, a backend API, database containers, workers, etc. corsa gives you:
|
|
10
10
|
|
|
11
11
|
- **Single dashboard** for all your processes with tabbed log viewing
|
|
12
12
|
- **Real-time logs** with search and ANSI color support
|
|
@@ -112,7 +112,7 @@ corsa mcp
|
|
|
112
112
|
|
|
113
113
|
## Configuration
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
corsa is configured via a TOML file. By default, it looks for `corsa.config.toml` in the current directory.
|
|
116
116
|
|
|
117
117
|
### Minimal Example
|
|
118
118
|
|
|
@@ -170,7 +170,7 @@ For a complete reference of all configuration options, see the [sample config fi
|
|
|
170
170
|
|
|
171
171
|
## Themes
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
corsa includes several built-in themes. Set in your config:
|
|
174
174
|
|
|
175
175
|
```toml
|
|
176
176
|
[ui]
|
|
@@ -181,7 +181,7 @@ Available themes: `default` (Moss), `mist`, `cappuccino`, `synthwave`, `terminal
|
|
|
181
181
|
|
|
182
182
|
## MCP Integration
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
corsa can expose an HTTP API for AI agents (Cursor, Claude, etc.) via the Model Context Protocol.
|
|
185
185
|
|
|
186
186
|
### Enable in Config
|
|
187
187
|
|
package/bin/corsa
CHANGED
|
@@ -10,6 +10,111 @@ const binDir = dirname(__filename);
|
|
|
10
10
|
const platform = process.platform;
|
|
11
11
|
const arch = process.arch;
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Detect which package manager was used based on lockfiles in the project root.
|
|
15
|
+
* Returns: "bun" | "pnpm" | "yarn" | "npm" | null
|
|
16
|
+
*/
|
|
17
|
+
function detectPackageManagerFromLockfile(projectRoot) {
|
|
18
|
+
if (!projectRoot) return null;
|
|
19
|
+
|
|
20
|
+
// Check in order of specificity
|
|
21
|
+
if (
|
|
22
|
+
existsSync(join(projectRoot, "bun.lockb")) ||
|
|
23
|
+
existsSync(join(projectRoot, "bun.lock"))
|
|
24
|
+
) {
|
|
25
|
+
return "bun";
|
|
26
|
+
}
|
|
27
|
+
if (existsSync(join(projectRoot, "pnpm-lock.yaml"))) {
|
|
28
|
+
return "pnpm";
|
|
29
|
+
}
|
|
30
|
+
if (existsSync(join(projectRoot, "yarn.lock"))) {
|
|
31
|
+
return "yarn";
|
|
32
|
+
}
|
|
33
|
+
if (existsSync(join(projectRoot, "package-lock.json"))) {
|
|
34
|
+
return "npm";
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Detect installation method from the wrapper's path.
|
|
41
|
+
* This is passed to the binary via environment variable since
|
|
42
|
+
* Bun-compiled binaries cannot determine their own filesystem path.
|
|
43
|
+
*
|
|
44
|
+
* Returns format: "package-manager-scope" (e.g., "npm-global", "pnpm-local")
|
|
45
|
+
*/
|
|
46
|
+
function detectInstallMethod() {
|
|
47
|
+
const wrapperPath = __filename;
|
|
48
|
+
|
|
49
|
+
// Check for bun global install (~/.bun/install/global/...)
|
|
50
|
+
if (
|
|
51
|
+
wrapperPath.includes("/.bun/") &&
|
|
52
|
+
wrapperPath.includes("/install/global/")
|
|
53
|
+
) {
|
|
54
|
+
return "bun-global";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check for pnpm global install (has /global/ in pnpm path)
|
|
58
|
+
if (
|
|
59
|
+
(wrapperPath.includes("/pnpm/") || wrapperPath.includes("/.pnpm/")) &&
|
|
60
|
+
wrapperPath.includes("/global/")
|
|
61
|
+
) {
|
|
62
|
+
return "pnpm-global";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Check for yarn global install
|
|
66
|
+
if (
|
|
67
|
+
(wrapperPath.includes("/yarn/") || wrapperPath.includes("/.yarn/")) &&
|
|
68
|
+
(wrapperPath.includes("/global/") || wrapperPath.includes("/.config/yarn/"))
|
|
69
|
+
) {
|
|
70
|
+
return "yarn-global";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check for npm-style global install (version managers, system paths)
|
|
74
|
+
if (wrapperPath.includes("/node_modules/")) {
|
|
75
|
+
const isGlobal =
|
|
76
|
+
wrapperPath.includes("/lib/node_modules/") ||
|
|
77
|
+
wrapperPath.includes("/usr/local/") ||
|
|
78
|
+
wrapperPath.includes("/.nvm/") ||
|
|
79
|
+
wrapperPath.includes("/mise/") ||
|
|
80
|
+
wrapperPath.includes("/volta/") ||
|
|
81
|
+
wrapperPath.includes("/fnm/") ||
|
|
82
|
+
wrapperPath.includes("/asdf/");
|
|
83
|
+
|
|
84
|
+
if (isGlobal) {
|
|
85
|
+
return "npm-global";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Local install - detect package manager from lockfile
|
|
89
|
+
// Find project root by going up from node_modules
|
|
90
|
+
const nodeModulesIndex = wrapperPath.indexOf("/node_modules/");
|
|
91
|
+
if (nodeModulesIndex !== -1) {
|
|
92
|
+
const projectRoot = wrapperPath.substring(0, nodeModulesIndex);
|
|
93
|
+
const pm = detectPackageManagerFromLockfile(projectRoot);
|
|
94
|
+
if (pm) {
|
|
95
|
+
return `${pm}-local`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Fallback to npm-local if no lockfile found
|
|
100
|
+
return "npm-local";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Fallback - shouldn't normally reach here via wrapper
|
|
104
|
+
return "unknown";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const installMethod = detectInstallMethod();
|
|
108
|
+
|
|
109
|
+
// For local installs, find the project root so the binary can run updates there
|
|
110
|
+
let projectRoot = null;
|
|
111
|
+
if (installMethod.endsWith("-local")) {
|
|
112
|
+
const nodeModulesIndex = __filename.indexOf("/node_modules/");
|
|
113
|
+
if (nodeModulesIndex !== -1) {
|
|
114
|
+
projectRoot = __filename.substring(0, nodeModulesIndex);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
13
118
|
// Map Node.js platform/arch to our binary names
|
|
14
119
|
const platformMap = {
|
|
15
120
|
darwin: "darwin",
|
|
@@ -43,9 +148,15 @@ if (!existsSync(binaryPath)) {
|
|
|
43
148
|
}
|
|
44
149
|
|
|
45
150
|
// Execute the binary with all arguments passed through
|
|
151
|
+
// Pass install method and project root via env vars since Bun binaries can't detect their own path
|
|
46
152
|
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
47
153
|
stdio: "inherit",
|
|
48
154
|
windowsHide: true,
|
|
155
|
+
env: {
|
|
156
|
+
...process.env,
|
|
157
|
+
CORSA_INSTALL_METHOD: installMethod,
|
|
158
|
+
...(projectRoot ? { CORSA_PROJECT_ROOT: projectRoot } : {}),
|
|
159
|
+
},
|
|
49
160
|
});
|
|
50
161
|
|
|
51
162
|
child.on("error", (err) => {
|
package/package.json
CHANGED
package/bin/corsa-linux-x64
DELETED
|
Binary file
|
package/bin/toolui
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
import { existsSync } from "node:fs";
|
|
5
|
-
import { dirname, join } from "node:path";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const binDir = dirname(__filename);
|
|
10
|
-
const platform = process.platform;
|
|
11
|
-
const arch = process.arch;
|
|
12
|
-
|
|
13
|
-
// Map Node.js platform/arch to our binary names
|
|
14
|
-
const platformMap = {
|
|
15
|
-
darwin: "darwin",
|
|
16
|
-
linux: "linux",
|
|
17
|
-
win32: "windows",
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const archMap = {
|
|
21
|
-
arm64: "arm64",
|
|
22
|
-
x64: "x64",
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const os = platformMap[platform];
|
|
26
|
-
const cpu = archMap[arch];
|
|
27
|
-
|
|
28
|
-
if (!os || !cpu) {
|
|
29
|
-
console.error(`Unsupported platform: ${platform}-${arch}`);
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const binaryName = `corsa-${os}-${cpu}${platform === "win32" ? ".exe" : ""}`;
|
|
34
|
-
const binaryPath = join(binDir, binaryName);
|
|
35
|
-
|
|
36
|
-
if (!existsSync(binaryPath)) {
|
|
37
|
-
console.error(`Binary not found: ${binaryPath}`);
|
|
38
|
-
console.error(
|
|
39
|
-
"This may happen if the postinstall script failed to download the binary.",
|
|
40
|
-
);
|
|
41
|
-
console.error("Try reinstalling: npm install -g corsa");
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Execute the binary with all arguments passed through
|
|
46
|
-
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
47
|
-
stdio: "inherit",
|
|
48
|
-
windowsHide: true,
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
child.on("error", (err) => {
|
|
52
|
-
console.error(`Failed to start corsa: ${err.message}`);
|
|
53
|
-
process.exit(1);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
child.on("exit", (code, signal) => {
|
|
57
|
-
if (signal) {
|
|
58
|
-
process.kill(process.pid, signal);
|
|
59
|
-
} else {
|
|
60
|
-
process.exit(code ?? 0);
|
|
61
|
-
}
|
|
62
|
-
});
|