agentp 0.2.0 → 0.4.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/README.md +38 -11
- package/bin/agentp +46 -18
- package/bin/ocmux +110 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# agentp
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/agentp)
|
|
4
|
+
[](https://www.npmjs.com/package/agentp)
|
|
5
|
+
[](https://www.npmjs.com/package/agentp)
|
|
6
|
+
[](https://www.npmjs.com/package/agentp)
|
|
7
|
+
|
|
3
8
|
This package provides two CLI tools:
|
|
4
9
|
|
|
5
10
|
- **`agentp`** — pipes prompt text into a running OpenCode TUI session and streams the assistant final answer back to stdout
|
|
@@ -34,23 +39,25 @@ npm link
|
|
|
34
39
|
Notes:
|
|
35
40
|
|
|
36
41
|
- `agentp` connects to the OpenCode event endpoint over HTTP.
|
|
37
|
-
- In practice this means running `opencode
|
|
38
|
-
- `opencode
|
|
42
|
+
- In practice this means running `opencode serve` (or equivalent serve mode) so the port is open.
|
|
43
|
+
- `opencode attach` is optional but useful to monitor the full conversation in another terminal/tmux pane.
|
|
44
|
+
- If the OpenCode server is password-protected (`OPENCODE_SERVER_PASSWORD`), both `agentp` and `ocmux` automatically send the required HTTP Basic Auth credentials.
|
|
39
45
|
|
|
40
46
|
## Usage
|
|
41
47
|
|
|
42
48
|
```bash
|
|
43
|
-
agentp [options] [
|
|
49
|
+
agentp [options] [url]
|
|
44
50
|
```
|
|
45
51
|
|
|
46
52
|
Options:
|
|
47
53
|
|
|
48
|
-
- `--qa`: print
|
|
54
|
+
- `--qa`: print the original prompt and answer with labels (useful when used as a filter)
|
|
55
|
+
- `--version`: show version
|
|
49
56
|
- `--help`: show help message
|
|
50
57
|
|
|
51
58
|
Arguments:
|
|
52
59
|
|
|
53
|
-
- `
|
|
60
|
+
- `url`: OpenCode TUI server URL or port number (defaults to `4096`). Examples: `4096`, `http://localhost:4096`, `http://192.168.1.50:4096`
|
|
54
61
|
|
|
55
62
|
## Examples
|
|
56
63
|
|
|
@@ -73,10 +80,10 @@ Send a multi-line prompt from a file:
|
|
|
73
80
|
cat prompt.txt | agentp
|
|
74
81
|
```
|
|
75
82
|
|
|
76
|
-
Use
|
|
83
|
+
Use an explicit port:
|
|
77
84
|
|
|
78
85
|
```bash
|
|
79
|
-
cat prompt.txt | agentp
|
|
86
|
+
cat prompt.txt | agentp 4096
|
|
80
87
|
```
|
|
81
88
|
|
|
82
89
|
Capture answer to a file:
|
|
@@ -85,13 +92,19 @@ Capture answer to a file:
|
|
|
85
92
|
cat prompt.txt | agentp > answer.txt
|
|
86
93
|
```
|
|
87
94
|
|
|
95
|
+
Connect to a remote OpenCode server:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
cat prompt.txt | agentp http://192.168.1.50:4096
|
|
99
|
+
```
|
|
100
|
+
|
|
88
101
|
From Vim/Neovim, send the current visual selection and replace it in place with the assistant answer:
|
|
89
102
|
|
|
90
103
|
```vim
|
|
91
104
|
:'<,'>!agentp
|
|
92
105
|
```
|
|
93
106
|
|
|
94
|
-
From Vim/Neovim, send the current visual selection and keep
|
|
107
|
+
From Vim/Neovim, send the current visual selection and keep the prompt with the answer:
|
|
95
108
|
|
|
96
109
|
```vim
|
|
97
110
|
:'<,'>!agentp --qa
|
|
@@ -109,9 +122,23 @@ Without arguments, searches upward from `<directory>` (default: `$PWD`) for `.oc
|
|
|
109
122
|
|
|
110
123
|
Subcommands:
|
|
111
124
|
|
|
112
|
-
- **`
|
|
125
|
+
- **`serve [--git|--GIT] [dir]`** — Create a server in `dir` (default: `$PWD`) and attach a TUI pane. Aliased as `new` for backwards compatibility. Errors if one already exists there. Warns if a parent directory already has a server.
|
|
126
|
+
- `--git` resolves `dir` to the nearest parent with a `.git` entry (file or dir); errors if none is found.
|
|
127
|
+
- `--GIT` resolves `dir` to the nearest parent with a `.git` directory only; errors if none is found.
|
|
113
128
|
- **`kill [dir]`** — Kill the server found upward from `dir`. Removes its tmux window and state file.
|
|
114
|
-
- **`list
|
|
129
|
+
- **`list`** — List all running servers with their directories, URLs, and status.
|
|
130
|
+
|
|
131
|
+
Options:
|
|
132
|
+
|
|
133
|
+
- `-l`: long output for default/serve commands (append `→ <dir>` after the URL)
|
|
134
|
+
- `--version`: show version
|
|
135
|
+
- `-h`: show help message
|
|
136
|
+
- `--`: treat the next argument as a directory even if it matches a subcommand name
|
|
137
|
+
|
|
138
|
+
Notes:
|
|
139
|
+
|
|
140
|
+
- If `<directory>` is not a valid path, `ocmux` tries to match it against the basenames of existing sessions (exact unique match).
|
|
141
|
+
- When using `--git`/`--GIT`, `ocmux` refuses to create a new server above an existing `.ocmux.json` found while searching for the git root.
|
|
115
142
|
|
|
116
143
|
### Requirements
|
|
117
144
|
|
|
@@ -133,7 +160,7 @@ Subcommands:
|
|
|
133
160
|
|
|
134
161
|
Operational hint:
|
|
135
162
|
|
|
136
|
-
- You can keep a separate `opencode
|
|
163
|
+
- You can keep a separate `opencode attach` view open to see the full run context while `agentp` is used from shell scripts or editor buffers.
|
|
137
164
|
|
|
138
165
|
## License
|
|
139
166
|
|
package/bin/agentp
CHANGED
|
@@ -4,12 +4,26 @@ const http = require('http');
|
|
|
4
4
|
const { URL } = require('url');
|
|
5
5
|
const { version } = require('../package.json');
|
|
6
6
|
|
|
7
|
+
function getAuthHeaders() {
|
|
8
|
+
const password = process.env.OPENCODE_SERVER_PASSWORD;
|
|
9
|
+
if (!password) return {};
|
|
10
|
+
const username = process.env.OPENCODE_SERVER_USERNAME || 'opencode';
|
|
11
|
+
const encoded = Buffer.from(`${username}:${password}`).toString('base64');
|
|
12
|
+
return { 'Authorization': `Basic ${encoded}` };
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
function makeRequest(options, data) {
|
|
8
16
|
return new Promise((resolve, reject) => {
|
|
9
17
|
const req = http.request(options, (res) => {
|
|
10
18
|
let body = '';
|
|
11
19
|
res.on('data', (chunk) => { body += chunk; });
|
|
12
|
-
res.on('end', () =>
|
|
20
|
+
res.on('end', () => {
|
|
21
|
+
if (res.statusCode === 401) {
|
|
22
|
+
reject(new Error('OpenCode server authentication failed. Set OPENCODE_SERVER_PASSWORD to match the server password.'));
|
|
23
|
+
} else {
|
|
24
|
+
resolve({ status: res.statusCode, body });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
13
27
|
});
|
|
14
28
|
req.on('error', reject);
|
|
15
29
|
if (data) req.write(data);
|
|
@@ -26,7 +40,8 @@ function buildJsonRequest(url, method, body) {
|
|
|
26
40
|
method: method,
|
|
27
41
|
headers: {
|
|
28
42
|
'Content-Type': 'application/json',
|
|
29
|
-
'Content-Length': Buffer.byteLength(body)
|
|
43
|
+
'Content-Length': Buffer.byteLength(body),
|
|
44
|
+
...getAuthHeaders()
|
|
30
45
|
}
|
|
31
46
|
};
|
|
32
47
|
}
|
|
@@ -47,8 +62,7 @@ async function submitPrompt(server) {
|
|
|
47
62
|
await makeRequest(buildJsonRequest(url, 'POST', '{}'), '{}');
|
|
48
63
|
}
|
|
49
64
|
|
|
50
|
-
async function sendPrompt(
|
|
51
|
-
const server = `http://localhost:${port}`;
|
|
65
|
+
async function sendPrompt(server) {
|
|
52
66
|
let promptText = '';
|
|
53
67
|
|
|
54
68
|
await clearPrompt(server);
|
|
@@ -71,9 +85,9 @@ const HUMAN_ROW = '👤:  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
|
|
|
71
85
|
const AGENT_ROW = '🤖:  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\n'
|
|
72
86
|
const FINAL_ROW = ' ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\n';
|
|
73
87
|
|
|
74
|
-
function listenForFinalAnswer(
|
|
88
|
+
function listenForFinalAnswer(server, showFinalRow) {
|
|
75
89
|
return new Promise((resolve, reject) => {
|
|
76
|
-
const url =
|
|
90
|
+
const url = `${server}/event`;
|
|
77
91
|
const parsed = new URL(url);
|
|
78
92
|
|
|
79
93
|
const req = http.request({
|
|
@@ -81,8 +95,13 @@ function listenForFinalAnswer(port, showFinalRow) {
|
|
|
81
95
|
port: parsed.port,
|
|
82
96
|
path: parsed.pathname,
|
|
83
97
|
method: 'GET',
|
|
84
|
-
headers: { 'Accept': 'text/event-stream' }
|
|
98
|
+
headers: { 'Accept': 'text/event-stream', ...getAuthHeaders() }
|
|
85
99
|
}, (res) => {
|
|
100
|
+
if (res.statusCode === 401) {
|
|
101
|
+
res.resume();
|
|
102
|
+
reject(new Error('OpenCode server authentication failed. Set OPENCODE_SERVER_PASSWORD to match the server password.'));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
86
105
|
let buffer = '';
|
|
87
106
|
const userMessageIDs = new Set();
|
|
88
107
|
|
|
@@ -155,7 +174,7 @@ function listenForFinalAnswer(port, showFinalRow) {
|
|
|
155
174
|
}
|
|
156
175
|
|
|
157
176
|
async function main() {
|
|
158
|
-
let
|
|
177
|
+
let serverBase = 'http://localhost:4096';
|
|
159
178
|
let qaMode = false;
|
|
160
179
|
|
|
161
180
|
const args = process.argv.slice(2);
|
|
@@ -164,33 +183,37 @@ async function main() {
|
|
|
164
183
|
console.log(version);
|
|
165
184
|
process.exit(0);
|
|
166
185
|
} else if (args[i] === '--help') {
|
|
167
|
-
console.error('Usage: agentp [options] [
|
|
186
|
+
console.error('Usage: agentp [options] [url]');
|
|
168
187
|
console.error('');
|
|
169
|
-
console.error('Pipe prompt text to an OpenCode TUI session
|
|
170
|
-
console.error('The assistant response is streamed to stdout.');
|
|
188
|
+
console.error('Pipe prompt text to an OpenCode TUI session and stream the assistant response to stdout.');
|
|
171
189
|
console.error('');
|
|
172
190
|
console.error('Options:');
|
|
173
191
|
console.error(' --version Show version');
|
|
174
|
-
console.error(' --qa Print
|
|
192
|
+
console.error(' --qa Print the original prompt and answer with labels (useful as a filter)');
|
|
175
193
|
console.error(' --help Show this help message');
|
|
176
194
|
console.error('');
|
|
177
195
|
console.error('Arguments:');
|
|
178
|
-
console.error('
|
|
196
|
+
console.error(' url OpenCode TUI server URL or port number (default: 4096)');
|
|
197
|
+
console.error(' Examples: 4096, http://localhost:4096, http://192.168.1.50:4096');
|
|
179
198
|
console.error('');
|
|
180
199
|
console.error('See also: ocmux -- manage OpenCode tmux sessions (ocmux --help)');
|
|
181
200
|
console.error('');
|
|
182
201
|
console.error('Examples:');
|
|
183
202
|
console.error(' printf "Summarize this file" | agentp');
|
|
184
|
-
console.error(' cat prompt.txt | agentp
|
|
203
|
+
console.error(' cat prompt.txt | agentp 4096');
|
|
204
|
+
console.error(' # Vim/Neovim filter, preserving prompt + answer:');
|
|
205
|
+
console.error(" :'<,'>!agentp --qa");
|
|
185
206
|
process.exit(0);
|
|
186
207
|
} else if (args[i] === '--qa') {
|
|
187
208
|
qaMode = true;
|
|
188
|
-
} else if (
|
|
189
|
-
|
|
209
|
+
} else if (args[i].includes('://')) {
|
|
210
|
+
serverBase = args[i].replace(/\/+$/, '');
|
|
211
|
+
} else if (/^\d+$/.test(args[i])) {
|
|
212
|
+
serverBase = 'http://localhost:' + args[i];
|
|
190
213
|
}
|
|
191
214
|
}
|
|
192
215
|
|
|
193
|
-
const promptText = await sendPrompt(
|
|
216
|
+
const promptText = await sendPrompt(serverBase);
|
|
194
217
|
|
|
195
218
|
if (qaMode) {
|
|
196
219
|
process.stdout.write(HUMAN_ROW);
|
|
@@ -198,7 +221,12 @@ async function main() {
|
|
|
198
221
|
process.stdout.write(AGENT_ROW);
|
|
199
222
|
}
|
|
200
223
|
|
|
201
|
-
await listenForFinalAnswer(
|
|
224
|
+
await listenForFinalAnswer(serverBase, qaMode);
|
|
225
|
+
|
|
226
|
+
if (!process.env.OPENCODE_SERVER_PASSWORD) {
|
|
227
|
+
process.stderr.write('\n');
|
|
228
|
+
process.stderr.write('🔓 ⚠️ Server is not password-protected! Set OPENCODE_SERVER_PASSWORD for better security.\n');
|
|
229
|
+
}
|
|
202
230
|
}
|
|
203
231
|
|
|
204
232
|
main().catch((err) => {
|
package/bin/ocmux
CHANGED
|
@@ -66,6 +66,52 @@ function findParentStatefile(startDir) {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
function findParentGitDir(startDir, requireDir) {
|
|
70
|
+
let dir = path.resolve(startDir);
|
|
71
|
+
let stateDir = null;
|
|
72
|
+
while (true) {
|
|
73
|
+
if (!stateDir && fs.existsSync(path.join(dir, '.ocmux.json'))) {
|
|
74
|
+
stateDir = dir;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const gitPath = path.join(dir, '.git');
|
|
78
|
+
if (fs.existsSync(gitPath)) {
|
|
79
|
+
if (!requireDir) {
|
|
80
|
+
return { dir, stateDir, gitPath };
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
if (fs.statSync(gitPath).isDirectory()) {
|
|
84
|
+
return { dir, stateDir, gitPath };
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
// ignore stat errors
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (dir === HOME || dir === path.dirname(dir)) return { dir: null, stateDir, gitPath: null };
|
|
92
|
+
dir = path.dirname(dir);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function resolveGitDir(startDir, requireDir, flagLabel) {
|
|
97
|
+
const result = findParentGitDir(startDir, requireDir);
|
|
98
|
+
if (!result.dir) {
|
|
99
|
+
const label = requireDir ? '.git directory' : '.git entry';
|
|
100
|
+
console.error(`Error: no ${label} found in ${startDir} or any of its parents.`);
|
|
101
|
+
console.error(`Run '${SCRIPT} serve' without ${flagLabel} to create a server in the current directory.`);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (result.stateDir && result.stateDir !== result.dir) {
|
|
106
|
+
console.error(`Error: found an existing ocmux server in ${result.stateDir}.`);
|
|
107
|
+
console.error(`Refusing to create a new server at ${result.dir} because it is above that session.`);
|
|
108
|
+
console.error(`Move outside ${result.stateDir} or pass an explicit directory.`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return result.dir;
|
|
113
|
+
}
|
|
114
|
+
|
|
69
115
|
function windowByDir(dir) {
|
|
70
116
|
const r = tmux(['list-windows', '-t', SESSION, '-F', '#{window_index}\t#{window_name}']);
|
|
71
117
|
if (r.status !== 0) return null;
|
|
@@ -187,9 +233,13 @@ function printHelp() {
|
|
|
187
233
|
console.error('that session is used.');
|
|
188
234
|
console.error('');
|
|
189
235
|
console.error('Subcommands:');
|
|
190
|
-
console.error('
|
|
236
|
+
console.error(' serve Create a server in <directory> (default: $PWD) and attach a TUI.');
|
|
237
|
+
console.error(' Aliased as "new" for backwards compatibility.');
|
|
191
238
|
console.error(' Errors if one already exists there.');
|
|
192
239
|
console.error(' Warns if a parent directory already has a server.');
|
|
240
|
+
console.error(' --git Use the nearest parent with a .git entry (file or dir).');
|
|
241
|
+
console.error(' --GIT Use the nearest parent with a .git directory only.');
|
|
242
|
+
console.error(' Refuses to create above an existing .ocmux.json found while searching.');
|
|
193
243
|
console.error(' kill Kill the server found upward from <directory>.');
|
|
194
244
|
console.error(' Removes its tmux window and state file.');
|
|
195
245
|
console.error(' list List all running servers with their directories and URLs.');
|
|
@@ -198,7 +248,7 @@ function printHelp() {
|
|
|
198
248
|
console.error(' a subcommand name (e.g. "ocmux -- kill").');
|
|
199
249
|
console.error('');
|
|
200
250
|
console.error('Options:');
|
|
201
|
-
console.error(' -l Long output: append " → <dir>" after the URL.');
|
|
251
|
+
console.error(' -l Long output: append " → <dir>" after the URL (serve/default).');
|
|
202
252
|
console.error(' --version Show version');
|
|
203
253
|
console.error(' -h Show this help');
|
|
204
254
|
console.error('');
|
|
@@ -226,7 +276,7 @@ function cmdSwitch(statefile, longMode) {
|
|
|
226
276
|
const found = windowByDir(serverDir);
|
|
227
277
|
if (found == null) {
|
|
228
278
|
console.error(`Error: tmux window for ${serverDir} not found`);
|
|
229
|
-
console.error("Run 'ocmux
|
|
279
|
+
console.error("Run 'ocmux serve' to create a new server.");
|
|
230
280
|
process.exit(1);
|
|
231
281
|
}
|
|
232
282
|
winIdx = found;
|
|
@@ -344,13 +394,25 @@ function doNew(targetDir, longMode) {
|
|
|
344
394
|
|
|
345
395
|
console.error(`Starting opencode server for ${targetDir}...`);
|
|
346
396
|
|
|
347
|
-
|
|
348
|
-
|
|
397
|
+
// Create window with interactive shell so .bashrc/.zshrc is sourced
|
|
398
|
+
const rc = tmux(['new-window', '-d', '-t', SESSION, '-n', targetDir, '-c', targetDir]);
|
|
349
399
|
if (rc.status !== 0) {
|
|
350
400
|
console.error(`Error: failed to create tmux window: ${rc.stderr}`);
|
|
351
401
|
process.exit(1);
|
|
352
402
|
}
|
|
353
403
|
|
|
404
|
+
const winIdx = windowByDir(targetDir);
|
|
405
|
+
if (winIdx == null) {
|
|
406
|
+
console.error('Error: window was not created properly');
|
|
407
|
+
process.exit(1);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
pinWindowName(winIdx);
|
|
411
|
+
|
|
412
|
+
// Send the command to the interactive shell (like typing it manually)
|
|
413
|
+
tmux(['send-keys', '-t', `${SESSION}:${winIdx}.0`,
|
|
414
|
+
`opencode serve --port 0 2>&1 | tee '${lf}'`, 'Enter']);
|
|
415
|
+
|
|
354
416
|
// Wait up to 10s for the URL in the logfile
|
|
355
417
|
let url = null;
|
|
356
418
|
for (let i = 0; i < 50; i++) {
|
|
@@ -367,21 +429,16 @@ function doNew(targetDir, longMode) {
|
|
|
367
429
|
process.exit(1);
|
|
368
430
|
}
|
|
369
431
|
|
|
370
|
-
const winIdx = windowByDir(targetDir);
|
|
371
|
-
if (winIdx == null) {
|
|
372
|
-
console.error('Error: window was not created properly');
|
|
373
|
-
process.exit(1);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
pinWindowName(winIdx);
|
|
377
|
-
|
|
378
432
|
const state = { url, logfile: lf, window_index: winIdx };
|
|
379
433
|
fs.writeFileSync(path.join(targetDir, '.ocmux.json'), JSON.stringify(state) + '\n');
|
|
380
434
|
|
|
381
|
-
tmux(['split-window', '-v', '-t', `${SESSION}:${winIdx}`, '-c', targetDir
|
|
382
|
-
|
|
435
|
+
const splitResult = tmux(['split-window', '-v', '-P', '-F', '#{pane_id}', '-t', `${SESSION}:${winIdx}`, '-c', targetDir]);
|
|
436
|
+
const tuiPane = splitResult.stdout.trim();
|
|
437
|
+
sleep(0.2);
|
|
438
|
+
tmux(['send-keys', '-t', tuiPane,
|
|
439
|
+
`opencode attach --continue '${url}'`, 'Enter']);
|
|
383
440
|
sleep(0.5);
|
|
384
|
-
|
|
441
|
+
if (tuiPane) tmux(['resize-pane', '-Z', '-t', tuiPane]);
|
|
385
442
|
|
|
386
443
|
const activeWin = activeWindowIndex();
|
|
387
444
|
if (activeWin !== winIdx) {
|
|
@@ -439,12 +496,25 @@ function cmdKill(statefile) {
|
|
|
439
496
|
|
|
440
497
|
function main() {
|
|
441
498
|
let longMode = false;
|
|
499
|
+
let gitMode = null;
|
|
442
500
|
const positional = [];
|
|
443
501
|
let dirIsForced = false;
|
|
444
502
|
|
|
445
503
|
for (const arg of process.argv.slice(2)) {
|
|
446
504
|
if (arg === '-l') {
|
|
447
505
|
longMode = true;
|
|
506
|
+
} else if (arg === '--git') {
|
|
507
|
+
if (gitMode && gitMode !== 'nearest') {
|
|
508
|
+
console.error("Error: cannot combine '--git' with '--GIT'.");
|
|
509
|
+
process.exit(1);
|
|
510
|
+
}
|
|
511
|
+
gitMode = 'nearest';
|
|
512
|
+
} else if (arg === '--GIT') {
|
|
513
|
+
if (gitMode && gitMode !== 'root') {
|
|
514
|
+
console.error("Error: cannot combine '--git' with '--GIT'.");
|
|
515
|
+
process.exit(1);
|
|
516
|
+
}
|
|
517
|
+
gitMode = 'root';
|
|
448
518
|
} else if (arg === '--version') {
|
|
449
519
|
console.log(version);
|
|
450
520
|
process.exit(0);
|
|
@@ -453,8 +523,10 @@ function main() {
|
|
|
453
523
|
} else if (arg === '--') {
|
|
454
524
|
dirIsForced = true;
|
|
455
525
|
} else if (arg.startsWith('-')) {
|
|
456
|
-
if (arg === '--new'
|
|
457
|
-
console.error(
|
|
526
|
+
if (arg === '--new') {
|
|
527
|
+
console.error("Error: '--new' is no longer a flag. Use 'ocmux serve' instead.");
|
|
528
|
+
} else if (arg === '--kill') {
|
|
529
|
+
console.error("Error: '--kill' is no longer a flag. Use 'ocmux kill' instead.");
|
|
458
530
|
} else {
|
|
459
531
|
console.error(`Error: unknown option '${arg}'`);
|
|
460
532
|
console.error(`Run '${SCRIPT} -h' for help.`);
|
|
@@ -476,7 +548,7 @@ function main() {
|
|
|
476
548
|
process.exit(1);
|
|
477
549
|
}
|
|
478
550
|
targetDir = resolveDir(arg);
|
|
479
|
-
} else if (arg === 'new' || arg === 'kill' || arg === 'list') {
|
|
551
|
+
} else if (arg === 'serve' || arg === 'new' || arg === 'kill' || arg === 'list') {
|
|
480
552
|
if (subcommand) {
|
|
481
553
|
console.error(`Error: multiple subcommands specified: '${subcommand}' and '${arg}'`);
|
|
482
554
|
process.exit(1);
|
|
@@ -495,27 +567,43 @@ function main() {
|
|
|
495
567
|
|
|
496
568
|
// Dispatch
|
|
497
569
|
if (subcommand === 'list') {
|
|
570
|
+
if (gitMode) {
|
|
571
|
+
console.error("Error: '--git' and '--GIT' are only valid with 'serve'.");
|
|
572
|
+
process.exit(1);
|
|
573
|
+
}
|
|
498
574
|
if (targetDir !== process.cwd()) {
|
|
499
575
|
console.error("Error: 'list' does not accept a directory path.");
|
|
500
576
|
process.exit(1);
|
|
501
577
|
}
|
|
502
578
|
cmdList();
|
|
503
579
|
} else if (subcommand === 'kill') {
|
|
580
|
+
if (gitMode) {
|
|
581
|
+
console.error("Error: '--git' and '--GIT' are only valid with 'serve'.");
|
|
582
|
+
process.exit(1);
|
|
583
|
+
}
|
|
504
584
|
const sf = findParentStatefile(targetDir);
|
|
505
585
|
if (!sf) {
|
|
506
586
|
console.error(`Error: no opencode server found in ${targetDir} or any of its parents.`);
|
|
507
|
-
console.error("Run 'ocmux
|
|
587
|
+
console.error("Run 'ocmux serve' to create a new server.");
|
|
508
588
|
process.exit(1);
|
|
509
589
|
}
|
|
510
590
|
cmdKill(sf);
|
|
511
|
-
} else if (subcommand === 'new') {
|
|
591
|
+
} else if (subcommand === 'serve' || subcommand === 'new') {
|
|
592
|
+
if (gitMode) {
|
|
593
|
+
const flagLabel = gitMode === 'root' ? '--GIT' : '--git';
|
|
594
|
+
targetDir = resolveGitDir(targetDir, gitMode === 'root', flagLabel);
|
|
595
|
+
}
|
|
512
596
|
cmdNew(targetDir, longMode);
|
|
513
597
|
} else {
|
|
598
|
+
if (gitMode) {
|
|
599
|
+
console.error("Error: '--git' and '--GIT' are only valid with 'serve'.");
|
|
600
|
+
process.exit(1);
|
|
601
|
+
}
|
|
514
602
|
// Default mode
|
|
515
603
|
const sf = findParentStatefile(targetDir);
|
|
516
604
|
if (!sf) {
|
|
517
605
|
console.error(`Error: no opencode server found in ${targetDir} or any of its parents.`);
|
|
518
|
-
console.error("Run 'ocmux
|
|
606
|
+
console.error("Run 'ocmux serve' to create a new server.");
|
|
519
607
|
process.exit(1);
|
|
520
608
|
}
|
|
521
609
|
ensureSession();
|