@berthdev/berth 0.0.0-stage → 0.2.1-rc.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/LICENSE +21 -0
- package/README.md +16 -2
- package/dist/src/agents/claude-name.d.ts +1 -0
- package/dist/src/agents/claude-name.js +57 -0
- package/dist/src/agents/claude-name.js.map +1 -0
- package/dist/src/agents/commands.d.ts +23 -0
- package/dist/src/agents/commands.js +48 -0
- package/dist/src/agents/commands.js.map +1 -0
- package/dist/src/agents/hooks.d.ts +13 -0
- package/dist/src/agents/hooks.js +141 -0
- package/dist/src/agents/hooks.js.map +1 -0
- package/dist/src/agents/registry.d.ts +33 -0
- package/dist/src/agents/registry.js +141 -0
- package/dist/src/agents/registry.js.map +1 -0
- package/dist/src/cli/main.d.ts +4 -0
- package/dist/src/cli/main.js +236 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/config/index.d.ts +21 -0
- package/dist/src/config/index.js +77 -0
- package/dist/src/config/index.js.map +1 -0
- package/dist/src/context.d.ts +9 -0
- package/dist/src/context.js +12 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/errors.d.ts +13 -0
- package/dist/src/errors.js +17 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/index.d.ts +17 -0
- package/dist/src/index.js +18 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/iterm2/install.d.ts +29 -0
- package/dist/src/iterm2/install.js +112 -0
- package/dist/src/iterm2/install.js.map +1 -0
- package/dist/src/present/attach.d.ts +16 -0
- package/dist/src/present/attach.js +31 -0
- package/dist/src/present/attach.js.map +1 -0
- package/dist/src/present/state.d.ts +14 -0
- package/dist/src/present/state.js +63 -0
- package/dist/src/present/state.js.map +1 -0
- package/dist/src/registry/doctor.d.ts +7 -0
- package/dist/src/registry/doctor.js +31 -0
- package/dist/src/registry/doctor.js.map +1 -0
- package/dist/src/registry/sessions.d.ts +47 -0
- package/dist/src/registry/sessions.js +138 -0
- package/dist/src/registry/sessions.js.map +1 -0
- package/dist/src/restore/ensure.d.ts +17 -0
- package/dist/src/restore/ensure.js +57 -0
- package/dist/src/restore/ensure.js.map +1 -0
- package/dist/src/restore/lock.d.ts +6 -0
- package/dist/src/restore/lock.js +74 -0
- package/dist/src/restore/lock.js.map +1 -0
- package/dist/src/shell.d.ts +6 -0
- package/dist/src/shell.js +68 -0
- package/dist/src/shell.js.map +1 -0
- package/dist/src/snapshot/store.d.ts +29 -0
- package/dist/src/snapshot/store.js +90 -0
- package/dist/src/snapshot/store.js.map +1 -0
- package/dist/src/snapshot/take.d.ts +5 -0
- package/dist/src/snapshot/take.js +33 -0
- package/dist/src/snapshot/take.js.map +1 -0
- package/dist/src/tmux/client.d.ts +49 -0
- package/dist/src/tmux/client.js +111 -0
- package/dist/src/tmux/client.js.map +1 -0
- package/dist/src/tmux/install.d.ts +4 -0
- package/dist/src/tmux/install.js +64 -0
- package/dist/src/tmux/install.js.map +1 -0
- package/dist/src/tools.d.ts +5 -0
- package/dist/src/tools.js +16 -0
- package/dist/src/tools.js.map +1 -0
- package/package.json +49 -4
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { parseArgs } from 'node:util';
|
|
7
|
+
import { createContext } from '../context.js';
|
|
8
|
+
import { BerthError, ExitCode } from '../errors.js';
|
|
9
|
+
import { handleHook, installHooks, uninstallHooks } from '../agents/hooks.js';
|
|
10
|
+
import { adopt, bellRang, checkOrigin, create, list, requireLive, stamp } from '../registry/sessions.js';
|
|
11
|
+
import { doctor } from '../registry/doctor.js';
|
|
12
|
+
import { takeSnapshot } from '../snapshot/take.js';
|
|
13
|
+
import { ensure, restoreFrom } from '../restore/ensure.js';
|
|
14
|
+
import { presentState } from '../present/state.js';
|
|
15
|
+
import { installTmux, uninstallTmux } from '../tmux/install.js';
|
|
16
|
+
import { installIterm2, reattach, uninstallIterm2 } from '../iterm2/install.js';
|
|
17
|
+
import { clientAttached } from '../present/attach.js';
|
|
18
|
+
import { toolsFor } from '../tools.js';
|
|
19
|
+
const USAGE = `usage: berth <command> [options]
|
|
20
|
+
|
|
21
|
+
ls [--json] all sessions
|
|
22
|
+
new <agent> [--name N] [--cwd D] [--origin O] [-- agent args]
|
|
23
|
+
adopt <session> --agent <id> [--origin O]
|
|
24
|
+
attach <session> [--origin O]
|
|
25
|
+
stamp [--session S] key=value...
|
|
26
|
+
snapshot
|
|
27
|
+
ensure [--origin O] [<session>]
|
|
28
|
+
restore [--origin O] [--session S] [<snapshot dir>]
|
|
29
|
+
show <session> [<snapshot dir>]
|
|
30
|
+
doctor [--json]
|
|
31
|
+
hooks install|uninstall <agent>
|
|
32
|
+
tmux install|uninstall snapshot hooks in ~/.tmux.conf
|
|
33
|
+
iterm2 install|uninstall Berth for iTerm2 (AutoLaunch script)
|
|
34
|
+
iterm2 reattach <session> rebuild if missing, print attach command
|
|
35
|
+
present <session> show its state on attached terminals
|
|
36
|
+
|
|
37
|
+
global: --socket <name> tmux -L socket (overrides BERTH_TMUX_SOCKET)
|
|
38
|
+
--socket-path <path> tmux -S socket (set by Berth's tmux hooks)
|
|
39
|
+
|
|
40
|
+
run by hooks: hook <agent> <event>, bell --session S, attached --session S --tty T --termtype X`;
|
|
41
|
+
function parse(args, options) {
|
|
42
|
+
try {
|
|
43
|
+
return parseArgs({ args, options, allowPositionals: true, strict: true });
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new BerthError(error.message, ExitCode.usage);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function need(value, what) {
|
|
50
|
+
if (!value)
|
|
51
|
+
throw new BerthError(`missing ${what}\n${USAGE}`, ExitCode.usage);
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
function formatRows(rows) {
|
|
55
|
+
const lines = rows.map((r) => [r.name, r.managed ? r.agent : '-', r.state || '-', r.managed ? r.origin : '-', r.live ? 'live' : 'stopped', r.conversation ? 'bound' : '-', r.cwd].join('\t'));
|
|
56
|
+
return ['NAME\tAGENT\tSTATE\tORIGIN\tLIVE\tCONV\tCWD', ...lines].join('\n');
|
|
57
|
+
}
|
|
58
|
+
function formatResults(results) {
|
|
59
|
+
if (results.length === 0)
|
|
60
|
+
return 'nothing to restore';
|
|
61
|
+
return results.map((r) => `${r.name}\t${r.outcome}${r.detail ? `\t${r.detail}` : ''}`).join('\n');
|
|
62
|
+
}
|
|
63
|
+
function cliPath() {
|
|
64
|
+
return realpathSync(fileURLToPath(import.meta.url));
|
|
65
|
+
}
|
|
66
|
+
function readStdin() {
|
|
67
|
+
try {
|
|
68
|
+
return readFileSync(0, 'utf8');
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return '';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function run(ctx, command, rest, out) {
|
|
75
|
+
switch (command) {
|
|
76
|
+
case 'ls': {
|
|
77
|
+
const { values } = parse(rest, { json: { type: 'boolean' } });
|
|
78
|
+
const rows = list(ctx);
|
|
79
|
+
out(values.json ? JSON.stringify(rows, null, 2) : formatRows(rows));
|
|
80
|
+
return ExitCode.ok;
|
|
81
|
+
}
|
|
82
|
+
case 'new': {
|
|
83
|
+
const dash = rest.indexOf('--');
|
|
84
|
+
const own = dash >= 0 ? rest.slice(0, dash) : rest;
|
|
85
|
+
const extraArgs = dash >= 0 ? rest.slice(dash + 1) : [];
|
|
86
|
+
const { values, positionals } = parse(own, { name: { type: 'string' }, cwd: { type: 'string' }, origin: { type: 'string' } });
|
|
87
|
+
const name = create(ctx, {
|
|
88
|
+
agent: need(positionals[0], 'agent'),
|
|
89
|
+
cwd: resolve(values.cwd ?? process.cwd()),
|
|
90
|
+
name: values.name,
|
|
91
|
+
origin: values.origin,
|
|
92
|
+
extraArgs,
|
|
93
|
+
});
|
|
94
|
+
out(name);
|
|
95
|
+
return ExitCode.ok;
|
|
96
|
+
}
|
|
97
|
+
case 'adopt': {
|
|
98
|
+
const { values, positionals } = parse(rest, { agent: { type: 'string' }, origin: { type: 'string' } });
|
|
99
|
+
adopt(ctx, need(positionals[0], 'session'), need(values.agent, '--agent'), values.origin);
|
|
100
|
+
return ExitCode.ok;
|
|
101
|
+
}
|
|
102
|
+
case 'attach': {
|
|
103
|
+
const { values, positionals } = parse(rest, { origin: { type: 'string' } });
|
|
104
|
+
const name = need(positionals[0], 'session');
|
|
105
|
+
requireLive(ctx, name);
|
|
106
|
+
if (values.origin)
|
|
107
|
+
stamp(ctx, name, { origin: checkOrigin(values.origin) });
|
|
108
|
+
const [bin, ...args] = ctx.tmux.attachArgv(name);
|
|
109
|
+
return spawnSync(bin, args, { stdio: 'inherit' }).status ?? ExitCode.failure;
|
|
110
|
+
}
|
|
111
|
+
case 'stamp': {
|
|
112
|
+
const { values, positionals } = parse(rest, { session: { type: 'string' } });
|
|
113
|
+
const pairs = Object.fromEntries(positionals.map((p) => {
|
|
114
|
+
const eq = p.indexOf('=');
|
|
115
|
+
if (eq <= 0)
|
|
116
|
+
throw new BerthError(`expected key=value, got ${p}`, ExitCode.usage);
|
|
117
|
+
return [p.slice(0, eq), p.slice(eq + 1)];
|
|
118
|
+
}));
|
|
119
|
+
const session = values.session ?? (process.env.TMUX_PANE ? ctx.tmux.sessionOfPane(process.env.TMUX_PANE) : undefined);
|
|
120
|
+
stamp(ctx, need(session, '--session (not inside a tmux pane)'), pairs);
|
|
121
|
+
return ExitCode.ok;
|
|
122
|
+
}
|
|
123
|
+
case 'snapshot':
|
|
124
|
+
out(takeSnapshot(ctx));
|
|
125
|
+
return ExitCode.ok;
|
|
126
|
+
case 'present': {
|
|
127
|
+
const name = need(rest[0], 'session');
|
|
128
|
+
requireLive(ctx, name);
|
|
129
|
+
const meta = ctx.tmux.listSessions().find((s) => s.name === name)?.meta ?? {};
|
|
130
|
+
out(String(presentState(ctx.tmux, name, meta.state ?? '', { note: meta.note })));
|
|
131
|
+
return ExitCode.ok;
|
|
132
|
+
}
|
|
133
|
+
case 'tmux': {
|
|
134
|
+
if (rest[0] === 'install')
|
|
135
|
+
out(installTmux(ctx.tmux, cliPath()));
|
|
136
|
+
else if (rest[0] === 'uninstall')
|
|
137
|
+
out(uninstallTmux(ctx.tmux));
|
|
138
|
+
else
|
|
139
|
+
throw new BerthError(`tmux: expected install or uninstall\n${USAGE}`, ExitCode.usage);
|
|
140
|
+
return ExitCode.ok;
|
|
141
|
+
}
|
|
142
|
+
case 'iterm2': {
|
|
143
|
+
if (rest[0] === 'install')
|
|
144
|
+
out(installIterm2(toolsFor(ctx, cliPath())).script);
|
|
145
|
+
else if (rest[0] === 'uninstall')
|
|
146
|
+
out(uninstallIterm2().script);
|
|
147
|
+
else if (rest[0] === 'reattach')
|
|
148
|
+
out(reattach(ctx, toolsFor(ctx, cliPath()), need(rest[1], 'session')));
|
|
149
|
+
else
|
|
150
|
+
throw new BerthError(`iterm2: expected install, uninstall or reattach\n${USAGE}`, ExitCode.usage);
|
|
151
|
+
return ExitCode.ok;
|
|
152
|
+
}
|
|
153
|
+
case 'attached': {
|
|
154
|
+
const { values } = parse(rest, { session: { type: 'string' }, tty: { type: 'string' }, termtype: { type: 'string' } });
|
|
155
|
+
clientAttached(ctx, { session: need(values.session, '--session'), tty: need(values.tty, '--tty'), termtype: values.termtype ?? '' });
|
|
156
|
+
return ExitCode.ok;
|
|
157
|
+
}
|
|
158
|
+
case 'ensure': {
|
|
159
|
+
const { values, positionals } = parse(rest, { origin: { type: 'string' } });
|
|
160
|
+
out(formatResults(ensure(ctx, { origin: values.origin, session: positionals[0] })));
|
|
161
|
+
return ExitCode.ok;
|
|
162
|
+
}
|
|
163
|
+
case 'restore': {
|
|
164
|
+
const { values, positionals } = parse(rest, { origin: { type: 'string' }, session: { type: 'string' } });
|
|
165
|
+
const snapshot = positionals[0] ? ctx.store.read(resolve(positionals[0])) : ctx.store.newest();
|
|
166
|
+
out(formatResults(restoreFrom(ctx, snapshot, { origin: values.origin, session: values.session })));
|
|
167
|
+
return ExitCode.ok;
|
|
168
|
+
}
|
|
169
|
+
case 'show': {
|
|
170
|
+
const { positionals } = parse(rest, {});
|
|
171
|
+
const dir = positionals[1] ? resolve(positionals[1]) : ctx.store.newest()?.dir;
|
|
172
|
+
if (!dir)
|
|
173
|
+
throw new BerthError('no snapshots yet', ExitCode.notFound);
|
|
174
|
+
out(ctx.store.scrollback(dir, need(positionals[0], 'session')));
|
|
175
|
+
return ExitCode.ok;
|
|
176
|
+
}
|
|
177
|
+
case 'doctor': {
|
|
178
|
+
const { values } = parse(rest, { json: { type: 'boolean' } });
|
|
179
|
+
const findings = doctor(ctx);
|
|
180
|
+
out(values.json ? JSON.stringify(findings, null, 2) : findings.map((f) => `${f.session ? `${f.session}: ` : ''}${f.problem}`).join('\n') || 'ok');
|
|
181
|
+
return ExitCode.ok;
|
|
182
|
+
}
|
|
183
|
+
case 'hooks': {
|
|
184
|
+
const [action, agent] = rest;
|
|
185
|
+
if (action === 'install')
|
|
186
|
+
out(installHooks(need(agent, 'agent'), cliPath()));
|
|
187
|
+
else if (action === 'uninstall')
|
|
188
|
+
out(uninstallHooks(need(agent, 'agent')));
|
|
189
|
+
else
|
|
190
|
+
throw new BerthError(`hooks: expected install or uninstall\n${USAGE}`, ExitCode.usage);
|
|
191
|
+
return ExitCode.ok;
|
|
192
|
+
}
|
|
193
|
+
case 'bell': {
|
|
194
|
+
const { values } = parse(rest, { session: { type: 'string' } });
|
|
195
|
+
bellRang(ctx, need(values.session, '--session'));
|
|
196
|
+
return ExitCode.ok;
|
|
197
|
+
}
|
|
198
|
+
case 'hook': {
|
|
199
|
+
handleHook(need(rest[0], 'agent'), need(rest[1], 'event'), readStdin());
|
|
200
|
+
return ExitCode.ok;
|
|
201
|
+
}
|
|
202
|
+
default:
|
|
203
|
+
throw new BerthError(USAGE, ExitCode.usage);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
export function main(argv, out = (t) => process.stdout.write(`${t}\n`)) {
|
|
207
|
+
const args = [...argv];
|
|
208
|
+
const env = { ...process.env };
|
|
209
|
+
for (const [flag, variable] of [['--socket', 'BERTH_TMUX_SOCKET'], ['--socket-path', 'BERTH_TMUX_SOCKET_PATH']]) {
|
|
210
|
+
const at = args.indexOf(flag);
|
|
211
|
+
if (at < 0)
|
|
212
|
+
continue;
|
|
213
|
+
env[variable] = need(args[at + 1], `${flag} value`);
|
|
214
|
+
args.splice(at, 2);
|
|
215
|
+
}
|
|
216
|
+
const [command, ...rest] = args;
|
|
217
|
+
if (!command || command === '-h' || command === '--help') {
|
|
218
|
+
out(USAGE);
|
|
219
|
+
return command ? ExitCode.ok : ExitCode.usage;
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
return run(createContext(env), command, rest, out);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
if (error instanceof BerthError) {
|
|
226
|
+
process.stderr.write(`berth: ${error.message}\n`);
|
|
227
|
+
return error.code;
|
|
228
|
+
}
|
|
229
|
+
process.stderr.write(`berth: ${error.message}\n`);
|
|
230
|
+
return ExitCode.failure;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (process.argv[1] && realpathSync(process.argv[1]) === cliPath()) {
|
|
234
|
+
process.exitCode = main(process.argv.slice(2));
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/cli/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface AgentOverride {
|
|
2
|
+
launch?: string;
|
|
3
|
+
resume?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface BerthConfig {
|
|
6
|
+
tmuxBin: string;
|
|
7
|
+
tmuxSocket?: string;
|
|
8
|
+
/** `-S` socket path; set by tmux hooks, which know the server they run in. */
|
|
9
|
+
tmuxSocketPath?: string;
|
|
10
|
+
stateDir: string;
|
|
11
|
+
snapshotRoot: string;
|
|
12
|
+
scrollbackLines: number;
|
|
13
|
+
retention: number;
|
|
14
|
+
agents: Record<string, AgentOverride>;
|
|
15
|
+
/** Command a restored iTerm2 pane runs to attach; `{session}` is the session name (spec §7.3). */
|
|
16
|
+
iterm2Attach?: string;
|
|
17
|
+
}
|
|
18
|
+
type Env = Record<string, string | undefined>;
|
|
19
|
+
/** Precedence: environment > config file > defaults (flags are applied by the CLI). */
|
|
20
|
+
export declare function loadConfig(env?: Env): BerthConfig;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { basename, join } from 'node:path';
|
|
4
|
+
import { parse } from 'smol-toml';
|
|
5
|
+
import { BerthError, ExitCode } from '../errors.js';
|
|
6
|
+
function configPath(env) {
|
|
7
|
+
if (env.BERTH_CONFIG)
|
|
8
|
+
return env.BERTH_CONFIG;
|
|
9
|
+
const base = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), '.config');
|
|
10
|
+
return join(base, 'berth', 'config.toml');
|
|
11
|
+
}
|
|
12
|
+
function defaultStateDir(env) {
|
|
13
|
+
const base = env.XDG_STATE_HOME || join(env.HOME || homedir(), '.local', 'state');
|
|
14
|
+
return join(base, 'berth');
|
|
15
|
+
}
|
|
16
|
+
function readFile(path) {
|
|
17
|
+
if (!existsSync(path))
|
|
18
|
+
return {};
|
|
19
|
+
try {
|
|
20
|
+
return parse(readFileSync(path, 'utf8'));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
throw new BerthError(`cannot read config ${path}: ${error.message}`, ExitCode.usage);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function positiveInt(value, name, fallback) {
|
|
27
|
+
if (value === undefined)
|
|
28
|
+
return fallback;
|
|
29
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 1) {
|
|
30
|
+
throw new BerthError(`config: ${name} must be a positive integer`, ExitCode.usage);
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
function agentOverrides(value) {
|
|
35
|
+
if (value === undefined)
|
|
36
|
+
return {};
|
|
37
|
+
if (typeof value !== 'object' || value === null)
|
|
38
|
+
throw new BerthError('config: [agents] must be a table', ExitCode.usage);
|
|
39
|
+
const result = {};
|
|
40
|
+
for (const [id, raw] of Object.entries(value)) {
|
|
41
|
+
const entry = raw;
|
|
42
|
+
for (const key of ['launch', 'resume']) {
|
|
43
|
+
if (entry[key] !== undefined && typeof entry[key] !== 'string') {
|
|
44
|
+
throw new BerthError(`config: agents.${id}.${key} must be a string`, ExitCode.usage);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
result[id] = { launch: entry.launch, resume: entry.resume };
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
/** Precedence: environment > config file > defaults (flags are applied by the CLI). */
|
|
52
|
+
export function loadConfig(env = process.env) {
|
|
53
|
+
const file = readFile(configPath(env));
|
|
54
|
+
const tmux = (file.tmux ?? {});
|
|
55
|
+
const snapshot = (file.snapshot ?? {});
|
|
56
|
+
const tmuxSocket = env.BERTH_TMUX_SOCKET || (typeof tmux.socket === 'string' ? tmux.socket : undefined);
|
|
57
|
+
const tmuxSocketPath = env.BERTH_TMUX_SOCKET_PATH || undefined;
|
|
58
|
+
// State is per tmux server: snapshots and locks of one server never restore
|
|
59
|
+
// into another. A server is named by its socket (its -L name is the basename).
|
|
60
|
+
const server = tmuxSocketPath ? basename(tmuxSocketPath) : (tmuxSocket ?? 'default');
|
|
61
|
+
const baseState = env.BERTH_STATE_DIR || (typeof file.state_dir === 'string' ? file.state_dir : defaultStateDir(env));
|
|
62
|
+
const stateDir = join(baseState, 'servers', server);
|
|
63
|
+
return {
|
|
64
|
+
tmuxBin: env.BERTH_TMUX || (typeof tmux.binary === 'string' ? tmux.binary : 'tmux'),
|
|
65
|
+
tmuxSocket,
|
|
66
|
+
tmuxSocketPath,
|
|
67
|
+
stateDir,
|
|
68
|
+
snapshotRoot: typeof snapshot.root === 'string' ? join(snapshot.root, server) : join(stateDir, 'snapshots'),
|
|
69
|
+
scrollbackLines: positiveInt(snapshot.scrollback_lines, 'snapshot.scrollback_lines', 3000),
|
|
70
|
+
retention: positiveInt(snapshot.keep, 'snapshot.keep', 5),
|
|
71
|
+
agents: agentOverrides(file.agents),
|
|
72
|
+
iterm2Attach: typeof file.iterm2?.attach === 'string'
|
|
73
|
+
? file.iterm2.attach
|
|
74
|
+
: undefined,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type BerthConfig } from './config/index.js';
|
|
2
|
+
import { SnapshotStore } from './snapshot/store.js';
|
|
3
|
+
import { Tmux } from './tmux/client.js';
|
|
4
|
+
export interface Context {
|
|
5
|
+
config: BerthConfig;
|
|
6
|
+
tmux: Tmux;
|
|
7
|
+
store: SnapshotStore;
|
|
8
|
+
}
|
|
9
|
+
export declare function createContext(env?: Record<string, string | undefined>): Context;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { loadConfig } from './config/index.js';
|
|
2
|
+
import { SnapshotStore } from './snapshot/store.js';
|
|
3
|
+
import { Tmux } from './tmux/client.js';
|
|
4
|
+
export function createContext(env = process.env) {
|
|
5
|
+
const config = loadConfig(env);
|
|
6
|
+
return {
|
|
7
|
+
config,
|
|
8
|
+
tmux: new Tmux({ bin: config.tmuxBin, socket: config.tmuxSocket, socketPath: config.tmuxSocketPath }),
|
|
9
|
+
store: new SnapshotStore(config.snapshotRoot),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAQxC,MAAM,UAAU,aAAa,CAAC,MAA0C,OAAO,CAAC,GAAG;IACjF,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC/B,OAAO;QACL,MAAM;QACN,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC;QACrG,KAAK,EAAE,IAAI,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC;KAC9C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Exit codes, see design/implementation.md §2.3. */
|
|
2
|
+
export declare const ExitCode: {
|
|
3
|
+
readonly ok: 0;
|
|
4
|
+
readonly failure: 1;
|
|
5
|
+
readonly usage: 2;
|
|
6
|
+
readonly tmuxUnavailable: 3;
|
|
7
|
+
readonly notFound: 4;
|
|
8
|
+
};
|
|
9
|
+
export type ExitCodeValue = (typeof ExitCode)[keyof typeof ExitCode];
|
|
10
|
+
export declare class BerthError extends Error {
|
|
11
|
+
readonly code: ExitCodeValue;
|
|
12
|
+
constructor(message: string, code?: ExitCodeValue);
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Exit codes, see design/implementation.md §2.3. */
|
|
2
|
+
export const ExitCode = {
|
|
3
|
+
ok: 0,
|
|
4
|
+
failure: 1,
|
|
5
|
+
usage: 2,
|
|
6
|
+
tmuxUnavailable: 3,
|
|
7
|
+
notFound: 4,
|
|
8
|
+
};
|
|
9
|
+
export class BerthError extends Error {
|
|
10
|
+
code;
|
|
11
|
+
constructor(message, code = ExitCode.failure) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.name = 'BerthError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,EAAE,EAAE,CAAC;IACL,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,CAAC;IACR,eAAe,EAAE,CAAC;IAClB,QAAQ,EAAE,CAAC;CACH,CAAC;AAIX,MAAM,OAAO,UAAW,SAAQ,KAAK;IACG;IAAtC,YAAY,OAAe,EAAW,OAAsB,QAAQ,CAAC,OAAO;QAC1E,KAAK,CAAC,OAAO,CAAC,CAAC;QADqB,SAAI,GAAJ,IAAI,CAAkC;QAE1E,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { createContext, type Context } from './context.js';
|
|
2
|
+
export { loadConfig, type BerthConfig } from './config/index.js';
|
|
3
|
+
export { BerthError, ExitCode } from './errors.js';
|
|
4
|
+
export { Tmux, type LiveSession, type SessionMeta } from './tmux/client.js';
|
|
5
|
+
export { ADAPTERS, getAdapter, type Adapter } from './agents/registry.js';
|
|
6
|
+
export { keptArgs, launchCommand, resumeCommand } from './agents/commands.js';
|
|
7
|
+
export { installHooks, uninstallHooks, handleHook, metaForEvent } from './agents/hooks.js';
|
|
8
|
+
export { list, create, adopt, stamp, bellRang, clearBell, BELL_NOTE, type SessionRow } from './registry/sessions.js';
|
|
9
|
+
export { doctor } from './registry/doctor.js';
|
|
10
|
+
export { SnapshotStore, type Snapshot, type SnapshotSession } from './snapshot/store.js';
|
|
11
|
+
export { takeSnapshot } from './snapshot/take.js';
|
|
12
|
+
export { ensure, restoreFrom, type RestoreResult } from './restore/ensure.js';
|
|
13
|
+
export { presentState, sequencesFor } from './present/state.js';
|
|
14
|
+
export { installTmux, uninstallTmux } from './tmux/install.js';
|
|
15
|
+
export { installIterm2, uninstallIterm2, reattach, attachCommand, autoLaunchScript, type Tools } from './iterm2/install.js';
|
|
16
|
+
export { clientAttached, PANE_VARIABLE } from './present/attach.js';
|
|
17
|
+
export { toolsFor, resolveBinary } from './tools.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { createContext } from './context.js';
|
|
2
|
+
export { loadConfig } from './config/index.js';
|
|
3
|
+
export { BerthError, ExitCode } from './errors.js';
|
|
4
|
+
export { Tmux } from './tmux/client.js';
|
|
5
|
+
export { ADAPTERS, getAdapter } from './agents/registry.js';
|
|
6
|
+
export { keptArgs, launchCommand, resumeCommand } from './agents/commands.js';
|
|
7
|
+
export { installHooks, uninstallHooks, handleHook, metaForEvent } from './agents/hooks.js';
|
|
8
|
+
export { list, create, adopt, stamp, bellRang, clearBell, BELL_NOTE } from './registry/sessions.js';
|
|
9
|
+
export { doctor } from './registry/doctor.js';
|
|
10
|
+
export { SnapshotStore } from './snapshot/store.js';
|
|
11
|
+
export { takeSnapshot } from './snapshot/take.js';
|
|
12
|
+
export { ensure, restoreFrom } from './restore/ensure.js';
|
|
13
|
+
export { presentState, sequencesFor } from './present/state.js';
|
|
14
|
+
export { installTmux, uninstallTmux } from './tmux/install.js';
|
|
15
|
+
export { installIterm2, uninstallIterm2, reattach, attachCommand, autoLaunchScript } from './iterm2/install.js';
|
|
16
|
+
export { clientAttached, PANE_VARIABLE } from './present/attach.js';
|
|
17
|
+
export { toolsFor, resolveBinary } from './tools.js';
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAgB,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,IAAI,EAAsC,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAgB,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAmB,MAAM,wBAAwB,CAAC;AACrH,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAuC,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAsB,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,EAAE,aAAa,EAAE,gBAAgB,EAAc,MAAM,qBAAqB,CAAC;AAC5H,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Context } from '../context.js';
|
|
2
|
+
/**
|
|
3
|
+
* Berth for iTerm2 (spec §7): iTerm2 restores windows, tabs and split panes;
|
|
4
|
+
* an AutoLaunch script puts each restored pane back in its session. iTerm2
|
|
5
|
+
* starts AutoLaunch scripts with a minimal PATH, so every tool is referenced
|
|
6
|
+
* by absolute path.
|
|
7
|
+
*/
|
|
8
|
+
export interface Iterm2Paths {
|
|
9
|
+
script: string;
|
|
10
|
+
/** Written by earlier versions (one profile per session); removed on install. */
|
|
11
|
+
legacyProfiles: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function iterm2Paths(env?: NodeJS.ProcessEnv): Iterm2Paths;
|
|
14
|
+
export interface Tools {
|
|
15
|
+
node: string;
|
|
16
|
+
cli: string;
|
|
17
|
+
tmux: string;
|
|
18
|
+
socket?: string;
|
|
19
|
+
/** PATH at install time: AutoLaunch scripts get a minimal one, without tmux or the agents. */
|
|
20
|
+
path?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Command that attaches a pane to `session`: the configured template, or tmux attach. */
|
|
23
|
+
export declare function attachCommand(ctx: Context, tools: Tools, session: string): string;
|
|
24
|
+
/** `berth iterm2 reattach <session>`: rebuild the session if missing, return its attach command. */
|
|
25
|
+
export declare function reattach(ctx: Context, tools: Tools, session: string): string;
|
|
26
|
+
export declare function autoLaunchScript(tools: Tools): string;
|
|
27
|
+
export declare function installIterm2(tools: Tools, env?: NodeJS.ProcessEnv): Iterm2Paths;
|
|
28
|
+
export declare function uninstallIterm2(env?: NodeJS.ProcessEnv): Iterm2Paths;
|
|
29
|
+
export declare function iterm2Installed(env?: NodeJS.ProcessEnv): boolean;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { BerthError, ExitCode } from '../errors.js';
|
|
5
|
+
import { ensure } from '../restore/ensure.js';
|
|
6
|
+
import { renderTemplate, shellQuote } from '../shell.js';
|
|
7
|
+
import { PANE_VARIABLE } from '../present/attach.js';
|
|
8
|
+
export function iterm2Paths(env = process.env) {
|
|
9
|
+
const base = join(env.HOME || homedir(), 'Library', 'Application Support', 'iTerm2');
|
|
10
|
+
return {
|
|
11
|
+
script: join(base, 'Scripts', 'AutoLaunch', 'berth.py'),
|
|
12
|
+
legacyProfiles: join(base, 'DynamicProfiles', 'berth.json'),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Command that attaches a pane to `session`: the configured template, or tmux attach. */
|
|
16
|
+
export function attachCommand(ctx, tools, session) {
|
|
17
|
+
const template = ctx.config.iterm2Attach;
|
|
18
|
+
if (template)
|
|
19
|
+
return renderTemplate(template, { session });
|
|
20
|
+
const socket = tools.socket ? ` -L ${shellQuote(tools.socket)}` : '';
|
|
21
|
+
return `${shellQuote(tools.tmux)}${socket} attach-session -t ${shellQuote(`=${session}`)}`;
|
|
22
|
+
}
|
|
23
|
+
/** `berth iterm2 reattach <session>`: rebuild the session if missing, return its attach command. */
|
|
24
|
+
export function reattach(ctx, tools, session) {
|
|
25
|
+
if (!ctx.tmux.hasSession(session))
|
|
26
|
+
ensure(ctx, { session });
|
|
27
|
+
if (!ctx.tmux.hasSession(session))
|
|
28
|
+
throw new BerthError(`cannot restore session ${session}`, ExitCode.notFound);
|
|
29
|
+
return attachCommand(ctx, tools, session);
|
|
30
|
+
}
|
|
31
|
+
export function autoLaunchScript(tools) {
|
|
32
|
+
const berth = JSON.stringify([tools.node, tools.cli, ...(tools.socket ? ['--socket', tools.socket] : [])]);
|
|
33
|
+
return `#!/usr/bin/env python3
|
|
34
|
+
"""Berth for iTerm2: put each pane iTerm2 restored back in its agent session.
|
|
35
|
+
|
|
36
|
+
Generated by \`berth iterm2 install\`; do not edit. iTerm2 restores the
|
|
37
|
+
windows, tabs and split panes; Berth marked each pane with the session it
|
|
38
|
+
showed (user variable ${PANE_VARIABLE}). This script asks the berth CLI to
|
|
39
|
+
rebuild a missing session and to give the command that attaches it, and
|
|
40
|
+
types that command into the pane. It holds no session logic.
|
|
41
|
+
"""
|
|
42
|
+
import asyncio
|
|
43
|
+
import os
|
|
44
|
+
import subprocess
|
|
45
|
+
|
|
46
|
+
import iterm2
|
|
47
|
+
|
|
48
|
+
BERTH = ${berth}
|
|
49
|
+
VARIABLE = "user.${PANE_VARIABLE}"
|
|
50
|
+
# Recorded at install: iTerm2's PATH for AutoLaunch has neither tmux nor the agent CLIs.
|
|
51
|
+
ENV = dict(os.environ, PATH=${JSON.stringify(tools.path ?? '')} or os.environ.get("PATH", ""))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def attach_command(session_name):
|
|
55
|
+
try:
|
|
56
|
+
done = subprocess.run(BERTH + ["iterm2", "reattach", session_name], capture_output=True, text=True, timeout=120, env=ENV)
|
|
57
|
+
except (OSError, subprocess.SubprocessError):
|
|
58
|
+
return None
|
|
59
|
+
command = done.stdout.strip()
|
|
60
|
+
return command if done.returncode == 0 and command else None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def panes(app):
|
|
64
|
+
for window in app.terminal_windows:
|
|
65
|
+
for tab in window.tabs:
|
|
66
|
+
for session in tab.sessions:
|
|
67
|
+
yield session
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def main(connection):
|
|
71
|
+
app = await iterm2.async_get_app(connection)
|
|
72
|
+
# Window restoration may still be running when AutoLaunch starts.
|
|
73
|
+
for _ in range(20):
|
|
74
|
+
if app.terminal_windows:
|
|
75
|
+
break
|
|
76
|
+
await asyncio.sleep(0.5)
|
|
77
|
+
await asyncio.sleep(1)
|
|
78
|
+
async for session in panes(app):
|
|
79
|
+
name = await session.async_get_variable(VARIABLE)
|
|
80
|
+
if not name:
|
|
81
|
+
continue
|
|
82
|
+
job = await session.async_get_variable("jobName")
|
|
83
|
+
if job == "tmux":
|
|
84
|
+
continue # still attached (iTerm2 kept the process alive)
|
|
85
|
+
command = await asyncio.get_running_loop().run_in_executor(None, attach_command, name)
|
|
86
|
+
if command:
|
|
87
|
+
await session.async_send_text(command + "\\n")
|
|
88
|
+
else:
|
|
89
|
+
# Shown on the screen only, never run by the shell.
|
|
90
|
+
await session.async_inject(("berth: could not restore session %s\\r\\n" % name).encode())
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
iterm2.run_until_complete(main)
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
export function installIterm2(tools, env = process.env) {
|
|
97
|
+
const paths = iterm2Paths(env);
|
|
98
|
+
mkdirSync(dirname(paths.script), { recursive: true });
|
|
99
|
+
writeFileSync(paths.script, autoLaunchScript(tools), { mode: 0o755 });
|
|
100
|
+
rmSync(paths.legacyProfiles, { force: true });
|
|
101
|
+
return paths;
|
|
102
|
+
}
|
|
103
|
+
export function uninstallIterm2(env = process.env) {
|
|
104
|
+
const paths = iterm2Paths(env);
|
|
105
|
+
rmSync(paths.script, { force: true });
|
|
106
|
+
rmSync(paths.legacyProfiles, { force: true });
|
|
107
|
+
return paths;
|
|
108
|
+
}
|
|
109
|
+
export function iterm2Installed(env = process.env) {
|
|
110
|
+
return existsSync(iterm2Paths(env).script);
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/iterm2/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Context } from '../context.js';
|
|
2
|
+
/** iTerm2 user variable naming the session a pane shows (spec §7.3). */
|
|
3
|
+
export declare const PANE_VARIABLE = "berthSession";
|
|
4
|
+
export declare function setUserVar(name: string, value: string): string;
|
|
5
|
+
export interface AttachedClient {
|
|
6
|
+
session: string;
|
|
7
|
+
tty: string;
|
|
8
|
+
termtype: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A client attached to a session, or switched to one (tmux hooks
|
|
12
|
+
* client-attached / client-session-changed). For iTerm2 the pane is marked
|
|
13
|
+
* with the session's name and the session's origin becomes iterm2; every
|
|
14
|
+
* client is shown the session's current state.
|
|
15
|
+
*/
|
|
16
|
+
export declare function clientAttached(ctx: Context, client: AttachedClient): void;
|