agentp 0.3.0 → 0.5.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/AGENTS.md +100 -8
- package/CHANGELOG.md +2 -0
- package/README.md +69 -3
- package/bin/agentp +17 -130
- package/bin/ocmux +50 -107
- package/bin/tgagentp +899 -0
- package/lib/ocmux.js +156 -0
- package/lib/opencode.js +270 -0
- package/package.json +3 -2
package/bin/ocmux
CHANGED
|
@@ -6,6 +6,16 @@ const crypto = require('crypto');
|
|
|
6
6
|
const readline = require('readline');
|
|
7
7
|
const { spawnSync, execSync } = require('child_process');
|
|
8
8
|
const os = require('os');
|
|
9
|
+
const {
|
|
10
|
+
readState,
|
|
11
|
+
statefileFor,
|
|
12
|
+
tuiPaneId,
|
|
13
|
+
windowByDir,
|
|
14
|
+
windowNameByIndex,
|
|
15
|
+
activeWindowIndex,
|
|
16
|
+
paneCount,
|
|
17
|
+
listServers,
|
|
18
|
+
} = require('../lib/ocmux');
|
|
9
19
|
|
|
10
20
|
// ---------------------------------------------------------------------------
|
|
11
21
|
// Constants
|
|
@@ -43,18 +53,6 @@ function logfileFor(dir) {
|
|
|
43
53
|
return `/tmp/opencode-serve-${hashDir(dir)}.log`;
|
|
44
54
|
}
|
|
45
55
|
|
|
46
|
-
function statefileFor(dir) {
|
|
47
|
-
return path.join(dir, '.ocmux.json');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function readState(file) {
|
|
51
|
-
try {
|
|
52
|
-
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
53
|
-
} catch {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
56
|
function findParentStatefile(startDir) {
|
|
59
57
|
let dir = path.resolve(startDir);
|
|
60
58
|
while (true) {
|
|
@@ -98,7 +96,7 @@ function resolveGitDir(startDir, requireDir, flagLabel) {
|
|
|
98
96
|
if (!result.dir) {
|
|
99
97
|
const label = requireDir ? '.git directory' : '.git entry';
|
|
100
98
|
console.error(`Error: no ${label} found in ${startDir} or any of its parents.`);
|
|
101
|
-
console.error(`Run '${SCRIPT}
|
|
99
|
+
console.error(`Run '${SCRIPT} serve' without ${flagLabel} to create a server in the current directory.`);
|
|
102
100
|
process.exit(1);
|
|
103
101
|
}
|
|
104
102
|
|
|
@@ -112,52 +110,6 @@ function resolveGitDir(startDir, requireDir, flagLabel) {
|
|
|
112
110
|
return result.dir;
|
|
113
111
|
}
|
|
114
112
|
|
|
115
|
-
function windowByDir(dir) {
|
|
116
|
-
const r = tmux(['list-windows', '-t', SESSION, '-F', '#{window_index}\t#{window_name}']);
|
|
117
|
-
if (r.status !== 0) return null;
|
|
118
|
-
for (const line of r.stdout.trim().split('\n').filter(Boolean)) {
|
|
119
|
-
const [idx, name] = line.split('\t');
|
|
120
|
-
if (name === dir) return parseInt(idx, 10);
|
|
121
|
-
}
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function windowNameByIndex(idx) {
|
|
126
|
-
const r = tmux(['list-windows', '-t', SESSION, '-F', '#{window_index}\t#{window_name}']);
|
|
127
|
-
if (r.status !== 0) return null;
|
|
128
|
-
for (const line of r.stdout.trim().split('\n').filter(Boolean)) {
|
|
129
|
-
const [wi, name] = line.split('\t');
|
|
130
|
-
if (parseInt(wi, 10) === idx) return name;
|
|
131
|
-
}
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function activeWindowIndex() {
|
|
136
|
-
const r = tmux(['list-windows', '-t', SESSION, '-F', '#{window_index} #{window_active}']);
|
|
137
|
-
if (r.status !== 0) return null;
|
|
138
|
-
for (const line of r.stdout.trim().split('\n').filter(Boolean)) {
|
|
139
|
-
const [idx, active] = line.split(' ');
|
|
140
|
-
if (active === '1') return parseInt(idx, 10);
|
|
141
|
-
}
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function tuiPaneId(windowIndex) {
|
|
146
|
-
const r = tmux(['list-panes', '-t', `${SESSION}:${windowIndex}`, '-F', '#{pane_id}\t#{pane_current_command}']);
|
|
147
|
-
if (r.status !== 0) return null;
|
|
148
|
-
for (const line of r.stdout.trim().split('\n').filter(Boolean)) {
|
|
149
|
-
const [paneId, cmd] = line.split('\t');
|
|
150
|
-
if (cmd === 'node') return paneId;
|
|
151
|
-
}
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function paneCount(windowIndex) {
|
|
156
|
-
const r = tmux(['list-panes', '-t', `${SESSION}:${windowIndex}`, '-F', '#{pane_id}']);
|
|
157
|
-
if (r.status !== 0) return 0;
|
|
158
|
-
return r.stdout.trim().split('\n').filter(Boolean).length;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
113
|
function findWindowsByBasename(name) {
|
|
162
114
|
const r = tmux(['list-windows', '-t', SESSION, '-F', '#{window_name}']);
|
|
163
115
|
if (r.status !== 0) return [];
|
|
@@ -233,7 +185,8 @@ function printHelp() {
|
|
|
233
185
|
console.error('that session is used.');
|
|
234
186
|
console.error('');
|
|
235
187
|
console.error('Subcommands:');
|
|
236
|
-
console.error('
|
|
188
|
+
console.error(' serve Create a server in <directory> (default: $PWD) and attach a TUI.');
|
|
189
|
+
console.error(' Aliased as "new" for backwards compatibility.');
|
|
237
190
|
console.error(' Errors if one already exists there.');
|
|
238
191
|
console.error(' Warns if a parent directory already has a server.');
|
|
239
192
|
console.error(' --git Use the nearest parent with a .git entry (file or dir).');
|
|
@@ -247,7 +200,7 @@ function printHelp() {
|
|
|
247
200
|
console.error(' a subcommand name (e.g. "ocmux -- kill").');
|
|
248
201
|
console.error('');
|
|
249
202
|
console.error('Options:');
|
|
250
|
-
console.error(' -l Long output: append " → <dir>" after the URL (
|
|
203
|
+
console.error(' -l Long output: append " → <dir>" after the URL (serve/default).');
|
|
251
204
|
console.error(' --version Show version');
|
|
252
205
|
console.error(' -h Show this help');
|
|
253
206
|
console.error('');
|
|
@@ -275,7 +228,7 @@ function cmdSwitch(statefile, longMode) {
|
|
|
275
228
|
const found = windowByDir(serverDir);
|
|
276
229
|
if (found == null) {
|
|
277
230
|
console.error(`Error: tmux window for ${serverDir} not found`);
|
|
278
|
-
console.error("Run 'ocmux
|
|
231
|
+
console.error("Run 'ocmux serve' to create a new server.");
|
|
279
232
|
process.exit(1);
|
|
280
233
|
}
|
|
281
234
|
winIdx = found;
|
|
@@ -315,29 +268,7 @@ function cmdSwitch(statefile, longMode) {
|
|
|
315
268
|
// ---------------------------------------------------------------------------
|
|
316
269
|
|
|
317
270
|
function cmdList() {
|
|
318
|
-
const
|
|
319
|
-
if (sessionR.status !== 0) {
|
|
320
|
-
console.log('No opencode servers running.');
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const listR = tmux(['list-windows', '-t', SESSION, '-F', '#{window_index}\t#{window_name}']);
|
|
325
|
-
if (listR.status !== 0 || !listR.stdout.trim()) {
|
|
326
|
-
console.log('No opencode servers running.');
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
const rows = [];
|
|
331
|
-
for (const line of listR.stdout.trim().split('\n').filter(Boolean)) {
|
|
332
|
-
const [idx, dir] = line.split('\t');
|
|
333
|
-
const sf = path.join(dir, '.ocmux.json');
|
|
334
|
-
if (!fs.existsSync(sf)) continue;
|
|
335
|
-
const state = readState(sf);
|
|
336
|
-
if (!state || !state.url) continue;
|
|
337
|
-
const tui = tuiPaneId(parseInt(idx, 10));
|
|
338
|
-
rows.push({ url: state.url, dir, status: tui ? 'alive' : 'dead' });
|
|
339
|
-
}
|
|
340
|
-
|
|
271
|
+
const rows = listServers();
|
|
341
272
|
if (rows.length === 0) {
|
|
342
273
|
console.log('No opencode servers running.');
|
|
343
274
|
return;
|
|
@@ -393,13 +324,28 @@ function doNew(targetDir, longMode) {
|
|
|
393
324
|
|
|
394
325
|
console.error(`Starting opencode server for ${targetDir}...`);
|
|
395
326
|
|
|
396
|
-
|
|
397
|
-
|
|
327
|
+
// Create window with interactive shell so .bashrc/.zshrc is sourced
|
|
328
|
+
const rc = tmux(['new-window', '-d', '-t', SESSION, '-n', targetDir, '-c', targetDir]);
|
|
398
329
|
if (rc.status !== 0) {
|
|
399
330
|
console.error(`Error: failed to create tmux window: ${rc.stderr}`);
|
|
400
331
|
process.exit(1);
|
|
401
332
|
}
|
|
402
333
|
|
|
334
|
+
const winIdx = windowByDir(targetDir);
|
|
335
|
+
if (winIdx == null) {
|
|
336
|
+
console.error('Error: window was not created properly');
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
pinWindowName(winIdx);
|
|
341
|
+
|
|
342
|
+
// Truncate log file so stale URL from previous run can't be matched by polling
|
|
343
|
+
try { fs.truncateSync(lf); } catch { /* may not exist yet */ }
|
|
344
|
+
|
|
345
|
+
// Send the command to the interactive shell (like typing it manually)
|
|
346
|
+
tmux(['send-keys', '-t', `${SESSION}:${winIdx}.0`,
|
|
347
|
+
`opencode serve --port 0 2>&1 | tee '${lf}'`, 'Enter']);
|
|
348
|
+
|
|
403
349
|
// Wait up to 10s for the URL in the logfile
|
|
404
350
|
let url = null;
|
|
405
351
|
for (let i = 0; i < 50; i++) {
|
|
@@ -416,21 +362,16 @@ function doNew(targetDir, longMode) {
|
|
|
416
362
|
process.exit(1);
|
|
417
363
|
}
|
|
418
364
|
|
|
419
|
-
const winIdx = windowByDir(targetDir);
|
|
420
|
-
if (winIdx == null) {
|
|
421
|
-
console.error('Error: window was not created properly');
|
|
422
|
-
process.exit(1);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
pinWindowName(winIdx);
|
|
426
|
-
|
|
427
365
|
const state = { url, logfile: lf, window_index: winIdx };
|
|
428
366
|
fs.writeFileSync(path.join(targetDir, '.ocmux.json'), JSON.stringify(state) + '\n');
|
|
429
367
|
|
|
430
|
-
tmux(['split-window', '-v', '-t', `${SESSION}:${winIdx}`, '-c', targetDir
|
|
431
|
-
|
|
368
|
+
const splitResult = tmux(['split-window', '-v', '-P', '-F', '#{pane_id}', '-t', `${SESSION}:${winIdx}`, '-c', targetDir]);
|
|
369
|
+
const tuiPane = splitResult.stdout.trim();
|
|
370
|
+
sleep(0.2);
|
|
371
|
+
tmux(['send-keys', '-t', tuiPane,
|
|
372
|
+
`opencode attach --continue '${url}'`, 'Enter']);
|
|
432
373
|
sleep(0.5);
|
|
433
|
-
|
|
374
|
+
if (tuiPane) tmux(['resize-pane', '-Z', '-t', tuiPane]);
|
|
434
375
|
|
|
435
376
|
const activeWin = activeWindowIndex();
|
|
436
377
|
if (activeWin !== winIdx) {
|
|
@@ -515,8 +456,10 @@ function main() {
|
|
|
515
456
|
} else if (arg === '--') {
|
|
516
457
|
dirIsForced = true;
|
|
517
458
|
} else if (arg.startsWith('-')) {
|
|
518
|
-
if (arg === '--new'
|
|
519
|
-
console.error(
|
|
459
|
+
if (arg === '--new') {
|
|
460
|
+
console.error("Error: '--new' is no longer a flag. Use 'ocmux serve' instead.");
|
|
461
|
+
} else if (arg === '--kill') {
|
|
462
|
+
console.error("Error: '--kill' is no longer a flag. Use 'ocmux kill' instead.");
|
|
520
463
|
} else {
|
|
521
464
|
console.error(`Error: unknown option '${arg}'`);
|
|
522
465
|
console.error(`Run '${SCRIPT} -h' for help.`);
|
|
@@ -538,7 +481,7 @@ function main() {
|
|
|
538
481
|
process.exit(1);
|
|
539
482
|
}
|
|
540
483
|
targetDir = resolveDir(arg);
|
|
541
|
-
} else if (arg === 'new' || arg === 'kill' || arg === 'list') {
|
|
484
|
+
} else if (arg === 'serve' || arg === 'new' || arg === 'kill' || arg === 'list') {
|
|
542
485
|
if (subcommand) {
|
|
543
486
|
console.error(`Error: multiple subcommands specified: '${subcommand}' and '${arg}'`);
|
|
544
487
|
process.exit(1);
|
|
@@ -558,7 +501,7 @@ function main() {
|
|
|
558
501
|
// Dispatch
|
|
559
502
|
if (subcommand === 'list') {
|
|
560
503
|
if (gitMode) {
|
|
561
|
-
console.error("Error: '--git' and '--GIT' are only valid with '
|
|
504
|
+
console.error("Error: '--git' and '--GIT' are only valid with 'serve'.");
|
|
562
505
|
process.exit(1);
|
|
563
506
|
}
|
|
564
507
|
if (targetDir !== process.cwd()) {
|
|
@@ -568,17 +511,17 @@ function main() {
|
|
|
568
511
|
cmdList();
|
|
569
512
|
} else if (subcommand === 'kill') {
|
|
570
513
|
if (gitMode) {
|
|
571
|
-
console.error("Error: '--git' and '--GIT' are only valid with '
|
|
514
|
+
console.error("Error: '--git' and '--GIT' are only valid with 'serve'.");
|
|
572
515
|
process.exit(1);
|
|
573
516
|
}
|
|
574
517
|
const sf = findParentStatefile(targetDir);
|
|
575
518
|
if (!sf) {
|
|
576
519
|
console.error(`Error: no opencode server found in ${targetDir} or any of its parents.`);
|
|
577
|
-
console.error("Run 'ocmux
|
|
520
|
+
console.error("Run 'ocmux serve' to create a new server.");
|
|
578
521
|
process.exit(1);
|
|
579
522
|
}
|
|
580
523
|
cmdKill(sf);
|
|
581
|
-
} else if (subcommand === 'new') {
|
|
524
|
+
} else if (subcommand === 'serve' || subcommand === 'new') {
|
|
582
525
|
if (gitMode) {
|
|
583
526
|
const flagLabel = gitMode === 'root' ? '--GIT' : '--git';
|
|
584
527
|
targetDir = resolveGitDir(targetDir, gitMode === 'root', flagLabel);
|
|
@@ -586,14 +529,14 @@ function main() {
|
|
|
586
529
|
cmdNew(targetDir, longMode);
|
|
587
530
|
} else {
|
|
588
531
|
if (gitMode) {
|
|
589
|
-
console.error("Error: '--git' and '--GIT' are only valid with '
|
|
532
|
+
console.error("Error: '--git' and '--GIT' are only valid with 'serve'.");
|
|
590
533
|
process.exit(1);
|
|
591
534
|
}
|
|
592
535
|
// Default mode
|
|
593
536
|
const sf = findParentStatefile(targetDir);
|
|
594
537
|
if (!sf) {
|
|
595
538
|
console.error(`Error: no opencode server found in ${targetDir} or any of its parents.`);
|
|
596
|
-
console.error("Run 'ocmux
|
|
539
|
+
console.error("Run 'ocmux serve' to create a new server.");
|
|
597
540
|
process.exit(1);
|
|
598
541
|
}
|
|
599
542
|
ensureSession();
|