@bytespell/shella 0.1.0 → 0.1.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 +16 -6
- package/bin/cli.js +63 -1
- package/dist/web/favicon.svg +4 -0
- package/dist/web/index.html +1 -1
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -9,11 +9,19 @@ Run multiple AI agents in parallel, persist sessions across devices, and manage
|
|
|
9
9
|
### npx (Developers)
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
+
# From your project directory
|
|
13
|
+
cd ~/myproject
|
|
12
14
|
npx @bytespell/shella
|
|
13
15
|
```
|
|
14
16
|
|
|
15
17
|
Opens at `http://localhost:3067`. Access from your phone using the LAN IP shown.
|
|
16
18
|
|
|
19
|
+
For multiple projects:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx @bytespell/shella --projects-dir ~/code
|
|
23
|
+
```
|
|
24
|
+
|
|
17
25
|
### Docker (Homelab)
|
|
18
26
|
|
|
19
27
|
```yaml
|
|
@@ -24,8 +32,9 @@ services:
|
|
|
24
32
|
ports:
|
|
25
33
|
- '3067:3067'
|
|
26
34
|
volumes:
|
|
27
|
-
-
|
|
35
|
+
- ~/code:/project
|
|
28
36
|
- shella-data:/root/.config/shella
|
|
37
|
+
command: ['--projects-dir', '/project']
|
|
29
38
|
restart: unless-stopped
|
|
30
39
|
|
|
31
40
|
volumes:
|
|
@@ -47,11 +56,12 @@ docker compose up -d
|
|
|
47
56
|
## CLI Options
|
|
48
57
|
|
|
49
58
|
```bash
|
|
50
|
-
shella
|
|
51
|
-
shella --
|
|
52
|
-
shella --
|
|
53
|
-
shella --
|
|
54
|
-
shella --
|
|
59
|
+
shella # Start (registers current directory)
|
|
60
|
+
shella --projects-dir ~/code # Register all subdirs of ~/code
|
|
61
|
+
shella --port 3000 # Custom web port
|
|
62
|
+
shella --opencode-port 5000 # Custom OpenCode port
|
|
63
|
+
shella --no-open # Don't open browser
|
|
64
|
+
shella --help # Show help
|
|
55
65
|
```
|
|
56
66
|
|
|
57
67
|
## Development
|
package/bin/cli.js
CHANGED
|
@@ -4,10 +4,17 @@ import { createOpencodeServer } from '@opencode-ai/sdk';
|
|
|
4
4
|
import { networkInterfaces } from 'os';
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
6
|
import { createConnection } from 'net';
|
|
7
|
+
import { existsSync, readdirSync } from 'fs';
|
|
7
8
|
import path from 'path';
|
|
8
9
|
import { fileURLToPath } from 'url';
|
|
9
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const VERSION = '0.1.
|
|
11
|
+
const VERSION = '0.1.2';
|
|
12
|
+
/**
|
|
13
|
+
* Check if running inside Docker container
|
|
14
|
+
*/
|
|
15
|
+
function isDocker() {
|
|
16
|
+
return existsSync('/.dockerenv');
|
|
17
|
+
}
|
|
11
18
|
/**
|
|
12
19
|
* Check if a port is available
|
|
13
20
|
*/
|
|
@@ -64,12 +71,63 @@ function printAccessBox(lanIp, port) {
|
|
|
64
71
|
+${border}+
|
|
65
72
|
`);
|
|
66
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Register projects with OpenCode
|
|
76
|
+
*/
|
|
77
|
+
async function registerProjects(opencodePort, projectsDir) {
|
|
78
|
+
const baseUrl = `http://localhost:${opencodePort}`;
|
|
79
|
+
if (projectsDir) {
|
|
80
|
+
// Resolve to absolute path
|
|
81
|
+
const absDir = path.resolve(projectsDir);
|
|
82
|
+
if (!existsSync(absDir)) {
|
|
83
|
+
console.error(`Error: Projects directory not found: ${absDir}`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
// Register each subdirectory
|
|
87
|
+
const entries = readdirSync(absDir, { withFileTypes: true });
|
|
88
|
+
const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith('.'));
|
|
89
|
+
if (dirs.length === 0) {
|
|
90
|
+
console.error(`Error: No project directories found in ${absDir}`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
for (const entry of dirs) {
|
|
94
|
+
const dir = path.join(absDir, entry.name);
|
|
95
|
+
try {
|
|
96
|
+
await fetch(`${baseUrl}/project?directory=${encodeURIComponent(dir)}`);
|
|
97
|
+
console.log(` Registered: ${entry.name}`);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
console.error(` Failed to register ${entry.name}: ${err}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Register cwd
|
|
106
|
+
const dir = process.cwd();
|
|
107
|
+
try {
|
|
108
|
+
await fetch(`${baseUrl}/project?directory=${encodeURIComponent(dir)}`);
|
|
109
|
+
console.log(` Registered: ${path.basename(dir)}`);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
console.error(` Failed to register ${path.basename(dir)}: ${err}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
67
116
|
let opencodeServer = null;
|
|
68
117
|
let expressProcess = null;
|
|
69
118
|
async function startCommand(options) {
|
|
70
119
|
const port = parseInt(options.port);
|
|
71
120
|
const opencodePort = parseInt(options.opencodePort);
|
|
72
121
|
console.log(`Shella v${VERSION}\n`);
|
|
122
|
+
// Docker requires --projects-dir
|
|
123
|
+
if (isDocker() && !options.projectsDir) {
|
|
124
|
+
console.error('Error: --projects-dir is required when running in Docker.\n');
|
|
125
|
+
console.error('Example:');
|
|
126
|
+
console.error(' docker run -v ~/code:/project shella --projects-dir /project\n');
|
|
127
|
+
console.error('Or in docker-compose.yml:');
|
|
128
|
+
console.error(' command: ["--projects-dir", "/project"]');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
73
131
|
// Check if ports are available
|
|
74
132
|
const webPortAvailable = await isPortAvailable(port);
|
|
75
133
|
const ocPortAvailable = await isPortAvailable(opencodePort);
|
|
@@ -106,6 +164,9 @@ async function startCommand(options) {
|
|
|
106
164
|
}
|
|
107
165
|
process.exit(1);
|
|
108
166
|
}
|
|
167
|
+
// Register projects with OpenCode
|
|
168
|
+
console.log('Registering projects...');
|
|
169
|
+
await registerProjects(opencodePort, options.projectsDir);
|
|
109
170
|
// Start Express server in production mode
|
|
110
171
|
console.log('Starting Shella server...');
|
|
111
172
|
const serverPath = path.join(__dirname, '..', 'dist', 'server', 'index.js');
|
|
@@ -186,6 +247,7 @@ program
|
|
|
186
247
|
.description('Start Shella')
|
|
187
248
|
.option('-p, --port <port>', 'Web server port', '3067')
|
|
188
249
|
.option('--opencode-port <port>', 'OpenCode server port', '4096')
|
|
250
|
+
.option('--projects-dir <path>', 'Directory containing projects (each subdirectory becomes a project)')
|
|
189
251
|
.option('--no-open', "Don't open browser automatically")
|
|
190
252
|
.action(startCommand);
|
|
191
253
|
program.parse();
|
package/dist/web/index.html
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<html lang="en" class="dark">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="/
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
6
|
<meta
|
|
7
7
|
name="viewport"
|
|
8
8
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytespell/shella",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Self-hosted AI coding agents. Access from your phone.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/bytespell-oss/shella"
|
|
9
|
+
},
|
|
6
10
|
"bin": {
|
|
7
|
-
"shella": "
|
|
11
|
+
"shella": "bin/cli.js"
|
|
8
12
|
},
|
|
9
13
|
"files": [
|
|
10
14
|
"dist/",
|