opencode-mux 0.1.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 +173 -0
- package/dist/args.d.ts +12 -0
- package/dist/args.js +114 -0
- package/dist/args.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +15 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +62 -0
- package/dist/cli.js +300 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +28 -0
- package/dist/config.js +119 -0
- package/dist/config.js.map +1 -0
- package/dist/exec.d.ts +12 -0
- package/dist/exec.js +28 -0
- package/dist/exec.js.map +1 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.js +29 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +22 -0
- package/dist/session.js +42 -0
- package/dist/session.js.map +1 -0
- package/dist/theme/builtin.d.ts +10 -0
- package/dist/theme/builtin.js +39 -0
- package/dist/theme/builtin.js.map +1 -0
- package/dist/theme.d.ts +26 -0
- package/dist/theme.js +250 -0
- package/dist/theme.js.map +1 -0
- package/dist/tmux.d.ts +37 -0
- package/dist/tmux.js +108 -0
- package/dist/tmux.js.map +1 -0
- package/dist/tracker.d.ts +49 -0
- package/dist/tracker.js +75 -0
- package/dist/tracker.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/dist/watch.d.ts +34 -0
- package/dist/watch.js +166 -0
- package/dist/watch.js.map +1 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 opencode-mux contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# opencode-mux
|
|
2
|
+
|
|
3
|
+
A drop-in wrapper for [opencode](https://opencode.ai) that opens tmux panes for
|
|
4
|
+
subagent sessions automatically — and makes the whole layout look coherent no
|
|
5
|
+
matter what your tmux is configured to look like.
|
|
6
|
+
|
|
7
|
+
When a session running inside opencode-mux spawns a subagent, a new tmux pane
|
|
8
|
+
opens showing that session live. When the subagent finishes, the pane closes
|
|
9
|
+
itself. The subagent panes run opencode's minimal `mini` frontend, so they show
|
|
10
|
+
nothing but the conversation — no tab bar, no agent switcher.
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
┌──────────────────────────────────────────┬──────────────┐
|
|
14
|
+
│ │ subagent │
|
|
15
|
+
│ main opencode session │ (mini UI) │
|
|
16
|
+
│ (full TUI) ├──────────────┤
|
|
17
|
+
│ │ subagent │
|
|
18
|
+
│ │ (mini UI) │
|
|
19
|
+
└──────────────────────────────────────────┴──────────────┘
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- **Drop-in** — run it exactly like `opencode`; every other argument is passed
|
|
23
|
+
through unchanged.
|
|
24
|
+
- **Zero config intrusion** — never writes tmux `-g` options and never touches
|
|
25
|
+
your `~/.tmux.conf`, `~/.config/opencode/cli.json`, or `tui.json`.
|
|
26
|
+
- **Coherent look** — pane borders are styled to match your active opencode
|
|
27
|
+
theme, and the session opts are scoped to the mux session only.
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
| Tool | Version |
|
|
32
|
+
| ---- | ------- |
|
|
33
|
+
| Node.js | >= 20 |
|
|
34
|
+
| tmux | 3.x (tested on 3.7) |
|
|
35
|
+
| opencode | v2 (2.0.0–2.0.5 and 2.0.6+ both supported) |
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
npm install -g opencode-mux
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
To use it as your default `opencode`:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
alias opencode="opencode-mux"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
(binaries are `opencode-mux`, version `0.1.0`)
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
opencode-mux [mux options] [opencode arguments...]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Examples
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
# Just work in the current directory
|
|
61
|
+
opencode-mux
|
|
62
|
+
|
|
63
|
+
# Continue an existing session
|
|
64
|
+
opencode-mux -s ses_abc123
|
|
65
|
+
|
|
66
|
+
# Pass opencode's own flags through, with a tiled pane layout
|
|
67
|
+
opencode-mux --layout tiled --model fast
|
|
68
|
+
|
|
69
|
+
# Running inside an existing tmux session also works — subagent panes are
|
|
70
|
+
# opened in the session you're already in, and already-running subagents are
|
|
71
|
+
# adopted without a startup flash.
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Mux options
|
|
75
|
+
|
|
76
|
+
| Option | Description | Default |
|
|
77
|
+
| ------ | ----------- | ------- |
|
|
78
|
+
| `--config <path>` | Config file to use | `~/.config/opencode-mux.conf` |
|
|
79
|
+
| `--layout <name>` | Pane layout: `main-vertical`, `main-horizontal`, `tiled`, `even-horizontal`, `even-vertical` | `main-vertical` |
|
|
80
|
+
| `--main-pane-size <pct>` | Main pane size for `main-*` layouts, percent of the window | `60` |
|
|
81
|
+
| `--close <mode>` | Pane lifecycle: `auto` (close when the subagent finishes) or `keep` | `auto` |
|
|
82
|
+
| `--grace <seconds>` | Delay before a finished subagent's pane closes | `1` |
|
|
83
|
+
| `--parent <id>` | Only spawn panes for children of this session | inferred from `-s`/`--session` |
|
|
84
|
+
| `--session-name <name>` | Owned tmux session name (when starting outside tmux) | `mux` |
|
|
85
|
+
| `-h`, `--help` | Show help | |
|
|
86
|
+
| `--version` | Print the version | |
|
|
87
|
+
|
|
88
|
+
Everything else (e.g. `--model`, `--agent`, `-i` prompts, a directory) is passed
|
|
89
|
+
through to opencode unchanged.
|
|
90
|
+
|
|
91
|
+
## Config file
|
|
92
|
+
|
|
93
|
+
`~/.config/opencode-mux.conf` (honors `XDG_CONFIG_HOME`) is optional and is
|
|
94
|
+
auto-created with defaults on first run. Keys are `key=value`, lines starting
|
|
95
|
+
with `#` are comments, and unknown keys are ignored for forward compatibility.
|
|
96
|
+
|
|
97
|
+
```ini
|
|
98
|
+
# opencode-mux configuration
|
|
99
|
+
session_name=mux
|
|
100
|
+
layout=main-vertical
|
|
101
|
+
main_pane_size=60
|
|
102
|
+
close_panes=auto
|
|
103
|
+
grace=1
|
|
104
|
+
parent=
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Precedence: **CLI flags > config file > defaults**.
|
|
108
|
+
|
|
109
|
+
## How it works
|
|
110
|
+
|
|
111
|
+
- **Outside tmux**: opencode-mux creates a detached tmux session named
|
|
112
|
+
`mux` (or `--session-name`), applies session-scoped `status off` and
|
|
113
|
+
`mouse on`, re-executes itself inside, and attaches. When the main TUI
|
|
114
|
+
exits, the owned session is cleaned up.
|
|
115
|
+
- **Inside tmux**: the wrapper runs as a watcher. It connects to the opencode
|
|
116
|
+
server (same background service you're already using), listens for
|
|
117
|
+
subagent-session events, opens a pane per child session via
|
|
118
|
+
`opencode mini -s <id>`, styles the pane borders to match the active
|
|
119
|
+
opencode theme, and closes panes once their session finishes (after the
|
|
120
|
+
grace period).
|
|
121
|
+
|
|
122
|
+
## Features
|
|
123
|
+
|
|
124
|
+
### Subagent panes without the chrome
|
|
125
|
+
|
|
126
|
+
Subagent panes attach with `opencode mini`, the minimal frontend: no top tab
|
|
127
|
+
strip, no agent switcher or model panel — just the conversation and a one-line
|
|
128
|
+
footer. It streams live updates while the subagent works, accepts input, and
|
|
129
|
+
stays attached until the watcher closes the pane.
|
|
130
|
+
|
|
131
|
+
### Theme-matched borders
|
|
132
|
+
|
|
133
|
+
Pane borders follow your active opencode theme (`border.default`, falling back
|
|
134
|
+
to the border color). Paraphrased, it looks good no matter what anyone has
|
|
135
|
+
configured their setup to look like:
|
|
136
|
+
|
|
137
|
+
- Theme is resolved from your opencode config: project
|
|
138
|
+
`.opencode/cli.json` → project `.opencode/tui.json` → global `cli.json` →
|
|
139
|
+
global `tui.json` (first match wins; v2 object and legacy string forms both
|
|
140
|
+
supported).
|
|
141
|
+
- Built-in themes come from a vendored table of all opencode v2.0.5 built-in
|
|
142
|
+
themes; custom theme files are resolved from your config/theme dirs.
|
|
143
|
+
- A `system` theme or an unknown/unresolvable theme results in **no border
|
|
144
|
+
styling** — never wrong colors.
|
|
145
|
+
- Config changes are re-applied live (checked on the watcher tick), and
|
|
146
|
+
borders are cleared when the theme becomes unresolvable. Zero tmux calls
|
|
147
|
+
while nothing changed.
|
|
148
|
+
|
|
149
|
+
### Never touches your setup
|
|
150
|
+
|
|
151
|
+
All tmux writes are runtime-scoped: window options are applied with `-w`
|
|
152
|
+
(pane-border colors) and session options with `-t <session>` (status/mouse).
|
|
153
|
+
No `-g`, no writes to `~/.tmux.conf`, no changes to your opencode configs.
|
|
154
|
+
|
|
155
|
+
## Requirements & compatibility
|
|
156
|
+
|
|
157
|
+
- Handles opencode's two v2 server generations transparently: 2.0.0–2.0.5
|
|
158
|
+
(`/api/status`) and 2.0.6+ (`/api/info`).
|
|
159
|
+
- Depends on tmux; fails with a clear error if tmux or opencode v2 is missing.
|
|
160
|
+
|
|
161
|
+
## Development
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
npm install
|
|
165
|
+
npm run check # typecheck (tsc --noEmit)
|
|
166
|
+
npm test # vitest suite
|
|
167
|
+
npm run build # tsc -p tsconfig.build.json → dist/
|
|
168
|
+
npm run smoke # e2e smoke (needs a staging environment; blocked locally)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT
|
package/dist/args.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CliFlags } from "./config.js";
|
|
2
|
+
export interface ParsedArgs {
|
|
3
|
+
flags: CliFlags;
|
|
4
|
+
configPath?: string;
|
|
5
|
+
help: boolean;
|
|
6
|
+
version: boolean;
|
|
7
|
+
watch: boolean;
|
|
8
|
+
passThrough: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
11
|
+
/** Returns the parent session id from pass-through `-s`/`--session` opencode args, else null. */
|
|
12
|
+
export declare function parentFromPassThrough(passThrough: string[]): string | null;
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const LAYOUTS = new Set(["main-vertical", "main-horizontal", "tiled", "even-horizontal", "even-vertical"]);
|
|
2
|
+
const CLOSE_MODES = new Set(["auto", "keep"]);
|
|
3
|
+
export function parseArgs(argv) {
|
|
4
|
+
const out = { flags: {}, help: false, version: false, watch: false, passThrough: [] };
|
|
5
|
+
for (let i = 0; i < argv.length; i++) {
|
|
6
|
+
const arg = argv[i];
|
|
7
|
+
if (arg === "--mux-watch") {
|
|
8
|
+
out.watch = true;
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (arg === "-h" || arg === "--help") {
|
|
12
|
+
out.help = true;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (arg === "--version") {
|
|
16
|
+
out.version = true;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const eq = arg.indexOf("=");
|
|
20
|
+
const name = eq >= 0 ? arg.slice(0, eq) : arg;
|
|
21
|
+
const inline = eq >= 0 ? arg.slice(eq + 1) : null;
|
|
22
|
+
const value = () => {
|
|
23
|
+
if (inline !== null)
|
|
24
|
+
return inline;
|
|
25
|
+
const next = argv[i + 1];
|
|
26
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
27
|
+
i++;
|
|
28
|
+
return next;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
};
|
|
32
|
+
switch (name) {
|
|
33
|
+
case "--config": {
|
|
34
|
+
const v = value();
|
|
35
|
+
if (v === null)
|
|
36
|
+
throw new Error("opencode-mux: --config requires a path");
|
|
37
|
+
out.configPath = v;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
case "--layout": {
|
|
41
|
+
const v = value();
|
|
42
|
+
if (v === null || !LAYOUTS.has(v)) {
|
|
43
|
+
throw new Error("opencode-mux: --layout requires main-vertical | main-horizontal | tiled | even-horizontal | even-vertical");
|
|
44
|
+
}
|
|
45
|
+
out.flags.layout = v;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
case "--main-pane-size": {
|
|
49
|
+
const v = value();
|
|
50
|
+
const n = v === null ? Number.NaN : Number(v);
|
|
51
|
+
if (!Number.isFinite(n) || n < 20 || n > 80) {
|
|
52
|
+
throw new Error("opencode-mux: --main-pane-size requires a number between 20 and 80");
|
|
53
|
+
}
|
|
54
|
+
out.flags.mainPaneSize = Math.floor(n);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case "--close": {
|
|
58
|
+
const v = value();
|
|
59
|
+
if (v === null || !CLOSE_MODES.has(v)) {
|
|
60
|
+
throw new Error("opencode-mux: --close requires auto | keep");
|
|
61
|
+
}
|
|
62
|
+
out.flags.closePanes = v;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case "--grace": {
|
|
66
|
+
const v = value();
|
|
67
|
+
const n = v === null ? Number.NaN : Number(v);
|
|
68
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
69
|
+
throw new Error("opencode-mux: --grace requires a non-negative number of seconds");
|
|
70
|
+
}
|
|
71
|
+
out.flags.graceSeconds = Math.floor(n);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case "--parent": {
|
|
75
|
+
const v = value();
|
|
76
|
+
if (v === null)
|
|
77
|
+
throw new Error("opencode-mux: --parent requires a session id");
|
|
78
|
+
out.flags.parent = v === "" ? null : v;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case "--session-name": {
|
|
82
|
+
const v = value();
|
|
83
|
+
if (v === null || v === "")
|
|
84
|
+
throw new Error("opencode-mux: --session-name requires a name");
|
|
85
|
+
out.flags.sessionName = v;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
if (name.startsWith("--mux-")) {
|
|
90
|
+
throw new Error(`opencode-mux: unknown flag ${name}`);
|
|
91
|
+
}
|
|
92
|
+
out.passThrough.push(arg);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
/** Returns the parent session id from pass-through `-s`/`--session` opencode args, else null. */
|
|
98
|
+
export function parentFromPassThrough(passThrough) {
|
|
99
|
+
for (let i = 0; i < passThrough.length; i++) {
|
|
100
|
+
const arg = passThrough[i];
|
|
101
|
+
if (arg === "-s" || arg === "--session") {
|
|
102
|
+
const next = passThrough[i + 1];
|
|
103
|
+
if (next !== undefined && next.startsWith("ses_"))
|
|
104
|
+
return next;
|
|
105
|
+
}
|
|
106
|
+
if (arg.startsWith("--session=")) {
|
|
107
|
+
const id = arg.slice("--session=".length);
|
|
108
|
+
if (id.startsWith("ses_"))
|
|
109
|
+
return id;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=args.js.map
|
package/dist/args.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
|
package/dist/bin.d.ts
ADDED
package/dist/bin.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { main, nodeSpawn } from "./cli.js";
|
|
4
|
+
try {
|
|
5
|
+
process.exitCode = await main(process.argv.slice(2), {
|
|
6
|
+
env: process.env,
|
|
7
|
+
spawn: nodeSpawn,
|
|
8
|
+
entryPath: fileURLToPath(new URL("./bin.js", import.meta.url)),
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
console.error(String(error));
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE3C,IAAI,CAAC;IACH,OAAO,CAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QACnD,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,KAAK,EAAE,SAAS;QAChB,SAAS,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KAC/D,CAAC,CAAC;AACL,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { CliFlags } from "./config.js";
|
|
2
|
+
import type { ConnectServerDeps, ServerConnection, ServiceGeneration } from "./server.js";
|
|
3
|
+
export interface SpawnResult {
|
|
4
|
+
ok: boolean;
|
|
5
|
+
code: number | null;
|
|
6
|
+
error: string | null;
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SpawnOpts {
|
|
11
|
+
stdoutFile?: string;
|
|
12
|
+
stderrFile?: string;
|
|
13
|
+
inherit?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface SpawnHandle {
|
|
16
|
+
pid: number | null;
|
|
17
|
+
done: Promise<SpawnResult>;
|
|
18
|
+
kill(): void;
|
|
19
|
+
}
|
|
20
|
+
export type SpawnFn = (command: string, args: string[], opts?: SpawnOpts) => SpawnHandle;
|
|
21
|
+
/** Real subprocess launcher with optional file redirects (never throws). */
|
|
22
|
+
export declare function nodeSpawn(command: string, args: string[], opts?: SpawnOpts): SpawnHandle;
|
|
23
|
+
export interface CliDeps {
|
|
24
|
+
env: NodeJS.ProcessEnv;
|
|
25
|
+
spawn: SpawnFn;
|
|
26
|
+
entryPath: string;
|
|
27
|
+
opencodeCommand?: string;
|
|
28
|
+
nodeCommand?: string;
|
|
29
|
+
tmuxCommand?: string;
|
|
30
|
+
cwd?: string;
|
|
31
|
+
/** Injectable so the watcher path is unit-testable; defaults to the real server connection. */
|
|
32
|
+
connectServer?: (deps?: ConnectServerDeps) => Promise<ServerConnection>;
|
|
33
|
+
}
|
|
34
|
+
export declare function usageText(): string;
|
|
35
|
+
export declare function watchLogPath(env: NodeJS.ProcessEnv): string;
|
|
36
|
+
/** Parses "2.0.5" or "opencode v2.0.5" into the major version; junk → null. */
|
|
37
|
+
export declare function parseMajorVersion(version: string): number | null;
|
|
38
|
+
/**
|
|
39
|
+
* Maps an opencode version to the client protocol generation it speaks: 2.0.0–2.0.5
|
|
40
|
+
* serve GET /api/status (legacy), 2.0.6+ serve GET /api/info (new). Each client
|
|
41
|
+
* generation's Service.ensure() probes only its own endpoint and stops a mismatched
|
|
42
|
+
* registered server, so the binary's generation must select the matching client.
|
|
43
|
+
* Returns null for unsupported or unparseable versions.
|
|
44
|
+
*/
|
|
45
|
+
export declare function serviceGeneration(version: string): ServiceGeneration | null;
|
|
46
|
+
/** Re-emits resolved CLI flags as mux argv (for the watcher child process). */
|
|
47
|
+
export declare function flagsToArgv(flags: CliFlags): string[];
|
|
48
|
+
/** argv for the spawned watcher process: marker + config path + explicit flags. */
|
|
49
|
+
export declare function buildWatcherArgv(opts: {
|
|
50
|
+
configPath?: string;
|
|
51
|
+
flags: CliFlags;
|
|
52
|
+
}): string[];
|
|
53
|
+
/** argv the re-exec'd instance runs inside the fresh tmux session. */
|
|
54
|
+
export declare function buildReexecArgv(entryPath: string, originalArgv: string[]): string[];
|
|
55
|
+
/** tmux new-session argv that boots the detached session and its command. */
|
|
56
|
+
export declare function buildNewSessionCommand(input: {
|
|
57
|
+
entryPath: string;
|
|
58
|
+
originalArgv: string[];
|
|
59
|
+
sessionName: string;
|
|
60
|
+
cwd: string;
|
|
61
|
+
}): string[];
|
|
62
|
+
export declare function main(argv: string[], deps: CliDeps): Promise<number>;
|