coban 0.2.1 → 0.3.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 +21 -0
- package/dist/cli.js +26 -1
- package/dist/config.js +42 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,6 +57,27 @@ npm install -g coban
|
|
|
57
57
|
cd ~/your-project && coban
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
### First run
|
|
61
|
+
|
|
62
|
+
On first start coban writes `~/.config/coban/config.json` and only offers what it finds: `claude`, `codex`,
|
|
63
|
+
`opencode` and `cursor-agent` that are on your `PATH`. A plain `claude` login (`~/.claude`) becomes profile `h1`.
|
|
64
|
+
Add more Claude accounts with `coban account add` (run it while the app is closed): it opens `claude` for the next
|
|
65
|
+
free profile (`h2`, `h3`...) in its own `~/.claude-hesapN` dir, you log in in the browser and type `/exit`, and the
|
|
66
|
+
profile is written to the config. Restart coban to see it. Edit the file any time, see Config below.
|
|
67
|
+
|
|
68
|
+
Note: the prompts coban gives the orchestrator and workers, and `coban --help`, are currently in Turkish.
|
|
69
|
+
|
|
70
|
+
### Troubleshooting
|
|
71
|
+
|
|
72
|
+
| symptom | fix |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `SyntaxError` / `ERR_UNKNOWN_FILE_EXTENSION` on start | Node is too old, check `node -v` (needs ≥ 24) |
|
|
75
|
+
| agent panel opens but asks to log in | log in once with that CLI (`claude`, `codex`...) outside coban, using the same `CLAUDE_CONFIG_DIR` as the profile |
|
|
76
|
+
| a profile is missing | its CLI is not on `PATH` when coban first ran: install it and delete the config file, or add the profile by hand |
|
|
77
|
+
| `coban çalışmıyor` from a `coban ...` command | open the TUI first with plain `coban`: those commands talk to the running app |
|
|
78
|
+
| stale socket after a crash | `rm ~/.coban/coban.sock` |
|
|
79
|
+
| Codex cannot talk to coban | its sandbox may block the unix socket, see Known gaps |
|
|
80
|
+
|
|
60
81
|
### From source (development)
|
|
61
82
|
|
|
62
83
|
In a clone, TypeScript runs directly with no build step:
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import net from 'node:net';
|
|
2
|
-
import
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { appendProfile, CONFIG_PATH, loadConfig, nextAccount, SOCK } from './config.js';
|
|
3
5
|
import { repoMap, snippets } from './context.js';
|
|
4
6
|
import { allUsage } from './usage.js';
|
|
5
7
|
const HELP = `coban — çok hesaplı agent orkestratörü
|
|
@@ -24,6 +26,7 @@ Agent'ların içinden (veya başka bir terminalden):
|
|
|
24
26
|
coban board [--limit n] ortak kutunun son notları
|
|
25
27
|
coban usage hesap kullanımı (5s %, 7g token, reset), en az harcayan başta
|
|
26
28
|
coban log [--limit n]
|
|
29
|
+
coban account add yeni Claude hesabı ekle: giriş yaptırır, profili config'e yazar (arayüz kapalıyken)
|
|
27
30
|
|
|
28
31
|
Her komut --json ile ham çıktı verir.`;
|
|
29
32
|
function call(method, params) {
|
|
@@ -74,6 +77,28 @@ async function main() {
|
|
|
74
77
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
75
78
|
const { pos, flags, ctx } = parse(rest);
|
|
76
79
|
const num = (v) => (v === undefined || v === true ? undefined : Number(v));
|
|
80
|
+
if (cmd === 'account') {
|
|
81
|
+
if (pos[0] !== 'add') {
|
|
82
|
+
console.log('kullanım: coban account add');
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
const cfg = loadConfig();
|
|
86
|
+
const { profile, dir } = nextAccount(cfg);
|
|
87
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
88
|
+
console.log(`Yeni hesap: ${profile.id} (${dir})\nClaude açılıyor: tarayıcıdan giriş yap, sonra /exit yaz.\n`);
|
|
89
|
+
const r = spawnSync('claude', [], { stdio: 'inherit', env: { ...process.env, CLAUDE_CONFIG_DIR: dir } });
|
|
90
|
+
if (r.error) {
|
|
91
|
+
console.error('claude bulunamadı: önce Claude Code kur (https://claude.com/claude-code)');
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
if (!fs.readdirSync(dir).length) {
|
|
95
|
+
console.error('Giriş yapılmamış görünüyor, profil eklenmedi. Tekrar dene: coban account add');
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
appendProfile(profile);
|
|
99
|
+
console.log(`\n${profile.id} eklendi (${CONFIG_PATH}). Arayüzü yeniden aç: coban`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
77
102
|
if (!cmd || cmd === '--cwd') {
|
|
78
103
|
if (cmd === '--cwd')
|
|
79
104
|
process.chdir(rest[0]);
|
package/dist/config.js
CHANGED
|
@@ -20,6 +20,47 @@ const claude = (n, model, color) => ({
|
|
|
20
20
|
env: { CLAUDE_CONFIG_DIR: `~/.claude-hesap${n}` },
|
|
21
21
|
color,
|
|
22
22
|
});
|
|
23
|
+
const COLORS = ['#F6D5C8', '#FFD9A0', '#C8E6C9', '#FFC8DD', '#BDE0FE', '#E2CFEA'];
|
|
24
|
+
// `coban account add`: the next free hN, its config dir, and a profile ready to append to the config file.
|
|
25
|
+
export function nextAccount(cfg) {
|
|
26
|
+
const used = new Set(cfg.profiles.map((p) => p.id));
|
|
27
|
+
let n = 1;
|
|
28
|
+
while (used.has(`h${n}`))
|
|
29
|
+
n++;
|
|
30
|
+
return { profile: claude(n, SONNET, COLORS[(n - 1) % COLORS.length]), dir: path.join(os.homedir(), `.claude-hesap${n}`) };
|
|
31
|
+
}
|
|
32
|
+
export function appendProfile(p) {
|
|
33
|
+
const raw = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); // raw file: keep `~` paths unexpanded
|
|
34
|
+
raw.profiles.push(p);
|
|
35
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(raw, null, 2) + '\n');
|
|
36
|
+
}
|
|
37
|
+
const onPath = (bin) => (process.env.PATH ?? '').split(path.delimiter).some((d) => d && fs.existsSync(path.join(d, bin)));
|
|
38
|
+
// First run on a fresh machine: only offer what is actually installed. A single `claude` login (~/.claude) becomes
|
|
39
|
+
// h1; extra accounts (h2, h3) appear only when their own config dir (~/.claude-hesapN) already exists.
|
|
40
|
+
export function detectConfig() {
|
|
41
|
+
const profiles = [];
|
|
42
|
+
if (onPath('claude')) {
|
|
43
|
+
const h1 = claude(1, OPUS, '#F6D5C8');
|
|
44
|
+
if (!fs.existsSync(path.join(os.homedir(), '.claude-hesap1')))
|
|
45
|
+
delete h1.env;
|
|
46
|
+
profiles.push(h1);
|
|
47
|
+
for (const n of [2, 3]) {
|
|
48
|
+
if (fs.existsSync(path.join(os.homedir(), `.claude-hesap${n}`)))
|
|
49
|
+
profiles.push(claude(n, SONNET, n === 2 ? '#FFD9A0' : '#C8E6C9'));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (const p of DEFAULT_CONFIG.profiles) {
|
|
53
|
+
if (!p.id.match(/^h\d$/) && onPath(p.cmd.split(' ')[0]))
|
|
54
|
+
profiles.push(p);
|
|
55
|
+
}
|
|
56
|
+
if (!profiles.length)
|
|
57
|
+
return DEFAULT_CONFIG; // nothing found: keep the full template to edit
|
|
58
|
+
const orchestrator = profiles[0].id;
|
|
59
|
+
const cfg = { ...DEFAULT_CONFIG, orchestrator, profiles };
|
|
60
|
+
if (!/^h\d$/.test(orchestrator))
|
|
61
|
+
delete cfg.orchestratorModel; // the Sonnet override is a Claude model id
|
|
62
|
+
return cfg;
|
|
63
|
+
}
|
|
23
64
|
export const DEFAULT_CONFIG = {
|
|
24
65
|
orchestrator: 'h1',
|
|
25
66
|
// Coordination is mostly reading and routing, so the orchestrator runs on Sonnet;
|
|
@@ -38,7 +79,7 @@ const expand = (v) => v.replace(/^~(?=\/|$)/, os.homedir());
|
|
|
38
79
|
export function loadConfig() {
|
|
39
80
|
if (!fs.existsSync(CONFIG_PATH)) {
|
|
40
81
|
fs.mkdirSync(path.dirname(CONFIG_PATH), { recursive: true });
|
|
41
|
-
fs.writeFileSync(CONFIG_PATH, JSON.stringify(
|
|
82
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(detectConfig(), null, 2) + '\n');
|
|
42
83
|
}
|
|
43
84
|
const cfg = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
44
85
|
for (const p of cfg.profiles) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coban",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A terminal app where one orchestrator agent dispatches work to many AI coding agents — several Claude Code accounts, Codex, OpenCode, Cursor — that message each other over a shared board.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"postinstall": "node -e \"for(const a of ['darwin-arm64','darwin-x64','linux-x64','linux-arm64']){try{require('fs').chmodSync(require.resolve('node-pty/package.json').replace('package.json','prebuilds/'+a+'/spawn-helper'),0o755)}catch{}}\"",
|