@polderlabs/bizar-omp 0.4.1 → 0.4.3
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 +137 -58
- package/dist/cli/agents-ink.d.ts +24 -0
- package/dist/cli/agents-ink.d.ts.map +1 -0
- package/dist/cli/agents-ink.js +297 -0
- package/dist/cli/agents-ink.js.map +1 -0
- package/dist/cli/agents.d.ts +10 -2
- package/dist/cli/agents.d.ts.map +1 -1
- package/dist/cli/agents.js +137 -90
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/directory-selector.d.ts +19 -0
- package/dist/cli/directory-selector.d.ts.map +1 -0
- package/dist/cli/directory-selector.js +103 -0
- package/dist/cli/directory-selector.js.map +1 -0
- package/dist/cli/install.d.ts +6 -2
- package/dist/cli/install.d.ts.map +1 -1
- package/dist/cli/install.js +21 -6
- package/dist/cli/install.js.map +1 -1
- package/dist/cli/main.js +46 -5
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/omb.js +9 -1
- package/dist/cli/omb.js.map +1 -1
- package/dist/cli/setup-ui.d.ts +38 -0
- package/dist/cli/setup-ui.d.ts.map +1 -0
- package/dist/cli/setup-ui.js +90 -0
- package/dist/cli/setup-ui.js.map +1 -0
- package/dist/omp/omb-sessions.d.ts +12 -0
- package/dist/omp/omb-sessions.d.ts.map +1 -1
- package/dist/omp/omb-sessions.js +148 -1
- package/dist/omp/omb-sessions.js.map +1 -1
- package/dist/omp/omb-settings.d.ts +10 -0
- package/dist/omp/omb-settings.d.ts.map +1 -0
- package/dist/omp/omb-settings.js +34 -0
- package/dist/omp/omb-settings.js.map +1 -0
- package/dist/omp/tmux.d.ts +6 -0
- package/dist/omp/tmux.d.ts.map +1 -1
- package/dist/omp/tmux.js +46 -10
- package/dist/omp/tmux.js.map +1 -1
- package/docs/assets/bizar-omp-banner.svg +20 -0
- package/docs/assets/sponsored-by-polderlabs.svg +9 -0
- package/docs/compatibility/baseline.json +1 -1
- package/docs/releases/0.4.2.md +9 -0
- package/docs/releases/0.4.3.md +6 -0
- package/docs/releases/native-stable.md +1 -1
- package/docs/releases/support-matrix.md +1 -1
- package/package.json +6 -1
- package/skills/bizar-omp/SKILL.md +4 -2
package/README.md
CHANGED
|
@@ -1,114 +1,193 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="docs/assets/bizar-omp-banner.svg" alt="BizarHarness OMP: autonomous engineering with evidence in the loop" width="100%" />
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
<p>
|
|
5
|
+
<a href="https://www.npmjs.com/package/@polderlabs/bizar-omp"><img src="https://img.shields.io/npm/v/%40polderlabs%2Fbizar-omp?style=flat-square&color=0f766e&label=npm" alt="npm version" /></a>
|
|
6
|
+
<a href="https://github.com/PolderLabsVOF/BizarHarness-OMP/releases"><img src="https://img.shields.io/github/v/release/PolderLabsVOF/BizarHarness-OMP?style=flat-square&color=2563eb&label=release" alt="latest GitHub release" /></a>
|
|
7
|
+
<img src="https://img.shields.io/badge/OMP-18.2.6-2563eb?style=flat-square" alt="OMP 18.2.6" />
|
|
8
|
+
<img src="https://img.shields.io/badge/Node.js-22%2B-18181b?style=flat-square&logo=nodedotjs&logoColor=5fa04e" alt="Node.js 22 or newer" />
|
|
9
|
+
<img src="https://img.shields.io/badge/Bun-1.3.14%2B-18181b?style=flat-square&logo=bun&logoColor=fbf0df" alt="Bun 1.3.14 or newer" />
|
|
10
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-18181b?style=flat-square" alt="MIT license" /></a>
|
|
11
|
+
</p>
|
|
4
12
|
|
|
5
|
-
|
|
13
|
+
<p><strong>Native autonomous engineering workflows for oh-my-pi.</strong><br />
|
|
14
|
+
Plan proportionally, run work in durable sessions, and finish with evidence you can inspect.</p>
|
|
15
|
+
</div>
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What Bizar OMP is
|
|
20
|
+
|
|
21
|
+
BizarHarness OMP is a native [oh-my-pi](https://github.com/can1357/oh-my-pi) extension for engineering work. It adds workflow intent, acceptance criteria, evidence freshness, specialist roles, scoped integration, and a durable session launcher while leaving OMP in charge of sessions, models, credentials, tools, task execution, and conversation state.
|
|
22
|
+
|
|
23
|
+
| You need | Bizar OMP provides |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| A sensible process | Focused, bounded, and full workflow tiers selected from task scope and risk |
|
|
26
|
+
| Background work | `omb` sessions inside tmux, with reconnect and a cross-project session hub |
|
|
27
|
+
| Useful specialists | Native Bizar roles for orchestration, planning, research, implementation, review, security, verification, and documentation |
|
|
28
|
+
| Stronger completion signals | Host-observed checks, fresh evidence, explicit acceptance, and serialized integration |
|
|
29
|
+
| A readable operator experience | Native Advisor enabled by default, visible active agents, hidden tool trace, and readable chat output |
|
|
30
|
+
|
|
31
|
+
Bizar does not start a second agent runtime, replace OMP's task system, or include a Claude Code, MCP, or background daemon compatibility layer.
|
|
8
32
|
|
|
9
33
|
## Install
|
|
10
34
|
|
|
11
|
-
|
|
35
|
+
### Full installation
|
|
36
|
+
|
|
37
|
+
The supported installation path configures the complete OMP experience in one command.
|
|
12
38
|
|
|
13
|
-
|
|
39
|
+
Requires Node.js 22 or newer, Bun 1.3.14 or newer, and OMP 18.2.6 on `PATH`. OMP 18.2.5 and 18.2.4 remain regression-qualified.
|
|
14
40
|
|
|
15
41
|
```sh
|
|
16
|
-
|
|
17
|
-
omp
|
|
42
|
+
npx --yes @polderlabs/bizar-omp@0.4.3 setup
|
|
18
43
|
```
|
|
19
44
|
|
|
20
|
-
|
|
45
|
+
The installer:
|
|
21
46
|
|
|
22
|
-
|
|
47
|
+
1. Installs the exact Bizar version globally, providing `omb` and `bizar-omp`.
|
|
48
|
+
2. Registers and enables the same version through `omp plugin install`.
|
|
49
|
+
3. Installs the extension, eight specialist agents, skills, rules, prompts, tools, and model roles.
|
|
50
|
+
4. Makes the Bizar Orchestrator the default conversation and enables OMP's native Advisor.
|
|
51
|
+
5. Applies the autonomous preset for background tasks, steerable Collab, visible agents, and readable output.
|
|
52
|
+
6. Writes a recoverable receipt and preserves credentials, provider choices, approval policies, and project overrides.
|
|
53
|
+
|
|
54
|
+
Interactive terminals show a phase dashboard with detected versions, applied settings, the recovery receipt, and the next commands. Preview or script the same operation with:
|
|
23
55
|
|
|
24
56
|
```sh
|
|
25
|
-
|
|
26
|
-
bizar-omp
|
|
27
|
-
omb
|
|
57
|
+
npx --yes @polderlabs/bizar-omp@0.4.3 setup --dry-run
|
|
58
|
+
npx --yes @polderlabs/bizar-omp@0.4.3 setup --json
|
|
28
59
|
```
|
|
29
60
|
|
|
30
|
-
|
|
61
|
+
### Start OMP
|
|
31
62
|
|
|
32
|
-
|
|
63
|
+
```sh
|
|
64
|
+
omp # normal direct OMP session
|
|
65
|
+
omb # durable tmux-backed session
|
|
66
|
+
omb agents # session hub and new-session selector
|
|
67
|
+
```
|
|
33
68
|
|
|
34
|
-
|
|
69
|
+
`omp` remains untouched. `omb` is the explicit durable mode. Plain `omb` reconnects to the most recently active live session in the current directory, or creates that directory's default session when none exists.
|
|
35
70
|
|
|
36
|
-
|
|
71
|
+
### Verify, repair, or remove
|
|
37
72
|
|
|
38
73
|
```sh
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
npm run verify:package
|
|
74
|
+
bizar-omp install-doctor
|
|
75
|
+
omp plugin list
|
|
76
|
+
omp plugin doctor
|
|
43
77
|
```
|
|
44
78
|
|
|
45
|
-
|
|
79
|
+
For a profile-scoped installation:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
npx --yes @polderlabs/bizar-omp@0.4.3 setup --profile bizar
|
|
83
|
+
omp --profile bizar
|
|
84
|
+
bizar-omp install-doctor --profile bizar
|
|
85
|
+
```
|
|
46
86
|
|
|
47
|
-
|
|
87
|
+
Each profile has its own OMP login and model configuration. Remove Bizar's plugin configuration with `bizar-omp uninstall`. The command restores settings only when they still contain the values Bizar applied, preserving later edits. Remove the global shell commands separately if needed:
|
|
48
88
|
|
|
49
|
-
|
|
89
|
+
```sh
|
|
90
|
+
npm uninstall --global @polderlabs/bizar-omp
|
|
91
|
+
```
|
|
50
92
|
|
|
51
|
-
|
|
93
|
+
### Extension-only installation
|
|
52
94
|
|
|
53
|
-
|
|
95
|
+
Use the native plugin command only when you already manage the shell commands yourself or only need the OMP extension:
|
|
54
96
|
|
|
55
|
-
|
|
97
|
+
```sh
|
|
98
|
+
omp plugin install @polderlabs/bizar-omp@0.4.3
|
|
99
|
+
```
|
|
56
100
|
|
|
57
|
-
|
|
101
|
+
This does not install `omb` or `bizar-omp` on `PATH`. For checkout development, use `omp plugin link /path/to/bizaromp`.
|
|
58
102
|
|
|
59
|
-
##
|
|
103
|
+
## How to work with Bizar
|
|
60
104
|
|
|
61
|
-
|
|
105
|
+
The main OMP conversation is the Bizar Orchestrator. It chooses the smallest method that fits the work:
|
|
62
106
|
|
|
63
|
-
|
|
107
|
+
| Tier | Use it for | Typical proof |
|
|
108
|
+
| --- | --- | --- |
|
|
109
|
+
| Focused | A local bug fix, styling change, typo, or narrow documentation edit | Targeted check, diff review, or smoke test |
|
|
110
|
+
| Bounded | A contained behavior change or small feature | Acceptance criteria and focused tests |
|
|
111
|
+
| Full | Security, architecture, migrations, concurrency, unknown regressions, or cross-cutting work | Planning, isolation, fresh evidence, review, and serialized integration |
|
|
64
112
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
113
|
+
The route can escalate when scope or risk changes. A focused task is still validated; it simply avoids ceremony that does not improve the result.
|
|
114
|
+
|
|
115
|
+
### Useful commands inside OMP
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
/bizar run <objective> start a bounded workflow
|
|
119
|
+
/bizar status inspect the active workflow
|
|
120
|
+
/bizar evidence inspect current proof
|
|
121
|
+
/bizar cancel cancel the active workflow
|
|
122
|
+
/bizar-models map available OMP models to Bizar roles
|
|
123
|
+
/bizar-models health validate Bizar role selectors without changing them
|
|
68
124
|
```
|
|
69
125
|
|
|
70
|
-
`
|
|
126
|
+
`/bizar-models` reads OMP's authenticated model catalog locally, assigns the best available selectors to Bizar roles, validates native resolution, and preserves unrelated roles.
|
|
71
127
|
|
|
72
|
-
|
|
128
|
+
## Durable sessions and the agents hub
|
|
129
|
+
|
|
130
|
+
Use `omb` when a session must keep running after the terminal client closes. OMP runs inside a Bizar-owned tmux host from the first process. The client can detach without stopping the host or its background agents.
|
|
73
131
|
|
|
74
132
|
```sh
|
|
75
133
|
omb new "Investigate the API timeout"
|
|
76
134
|
omb --session api-retries
|
|
77
|
-
omb
|
|
135
|
+
omb agents
|
|
78
136
|
```
|
|
79
137
|
|
|
80
|
-
|
|
138
|
+
The `omb agents` hub provides:
|
|
81
139
|
|
|
82
|
-
|
|
140
|
+
- A workspace rail and grouped session list across projects.
|
|
141
|
+
- Session attach, rename, delete, refresh, settings, help, and reconnect controls.
|
|
142
|
+
- A new-session selector opened with `n`.
|
|
143
|
+
- Fuzzy directory search, parent and child navigation, recent project shortcuts, and direct absolute paths.
|
|
144
|
+
- Optional initial task text and session naming without leaving the TUI.
|
|
145
|
+
- Native OMP session titles with deterministic project/task fallbacks.
|
|
83
146
|
|
|
84
|
-
|
|
85
|
-
omb agents
|
|
86
|
-
```
|
|
147
|
+
Key controls are `Enter` attach, `n` new session, `e` rename, `d` delete, `s` settings, `r` refresh, `?` help, and `q` exit. Inside a session, `Ctrl-d` or `Ctrl-c` returns to the hub without sending EOF or interrupt input to OMP. Mouse scrolling is enabled by default and can be changed in Settings.
|
|
87
148
|
|
|
88
|
-
|
|
149
|
+
## Model roles and native assets
|
|
89
150
|
|
|
90
|
-
|
|
151
|
+
Bizar registers visible `bizar_*` roles without replacing operator-owned mappings. The Orchestrator and Advisor are enabled by default; specialist definitions are available to OMP when the orchestrator delegates work. Installation also provides the `bizar-omp` orientation skill, workflow skills, native rules, prompts, tools, and the OMP-native development reference skill.
|
|
91
152
|
|
|
92
|
-
|
|
93
|
-
omp collab list
|
|
94
|
-
omp collab link <instance-id-or-pid> # prints a control link
|
|
95
|
-
omp join "<control-link>"
|
|
96
|
-
```
|
|
153
|
+
## Security boundaries
|
|
97
154
|
|
|
98
|
-
|
|
155
|
+
- Bizar is trusted in-process extension code. OMP extensions and shell commands are not an OS sandbox.
|
|
156
|
+
- API keys, tokens, `.env` contents, and private control links are never written to source, logs, commits, or evidence.
|
|
157
|
+
- Credentials, providers, explicit tool policies, model selections, and project overrides remain owned by OMP.
|
|
158
|
+
- Managed worker changes remain isolated and are retained for explicit parent integration. Bizar does not silently apply worker patches.
|
|
159
|
+
- Install only in projects and environments you trust. Review changes with `setup --dry-run` and `bizar-omp install-doctor`.
|
|
99
160
|
|
|
100
|
-
|
|
161
|
+
## Compatibility
|
|
101
162
|
|
|
102
|
-
|
|
163
|
+
| Component | Qualified baseline |
|
|
164
|
+
| --- | --- |
|
|
165
|
+
| OMP | 18.2.6, with 18.2.5 and 18.2.4 regression-qualified |
|
|
166
|
+
| Node.js | 22.x and 24.x in CI |
|
|
167
|
+
| Bun | 1.3.14 compatibility job |
|
|
168
|
+
| Package | `@polderlabs/bizar-omp@0.4.3` |
|
|
103
169
|
|
|
104
|
-
|
|
170
|
+
Upstream OMP changes require a new compatibility baseline and contract run. The package does not widen a version range to hide an unqualified runtime.
|
|
105
171
|
|
|
106
|
-
##
|
|
172
|
+
## Development
|
|
107
173
|
|
|
108
|
-
|
|
174
|
+
```sh
|
|
175
|
+
npm install
|
|
176
|
+
make verify
|
|
177
|
+
```
|
|
109
178
|
|
|
110
|
-
|
|
111
|
-
|
|
179
|
+
The verification gate runs typecheck, tests, package checks, generated documentation checks, and secret scanning. A native OMP qualification run is also available when the OMP source package is installed:
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
npm run verify:omp
|
|
112
183
|
```
|
|
113
184
|
|
|
114
|
-
|
|
185
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for branches, Conventional Commits, release tags, and trusted publishing. Release notes live in [`docs/releases/`](docs/releases/).
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
MIT. See [LICENSE](LICENSE).
|
|
190
|
+
|
|
191
|
+
<div align="center">
|
|
192
|
+
<a href="https://polderlabs.io/"><img src="docs/assets/sponsored-by-polderlabs.svg" alt="Sponsored by PolderLabs" width="100%" /></a>
|
|
193
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type OmbSessionInfo } from "../omp/omb-sessions.js";
|
|
2
|
+
import type { OmbSettings } from "../omp/omb-settings.js";
|
|
3
|
+
export type AgentsHubAction = {
|
|
4
|
+
type: "quit";
|
|
5
|
+
} | {
|
|
6
|
+
type: "attach";
|
|
7
|
+
session: OmbSessionInfo;
|
|
8
|
+
} | {
|
|
9
|
+
type: "new";
|
|
10
|
+
projectCwd: string;
|
|
11
|
+
task: string;
|
|
12
|
+
sessionId: string;
|
|
13
|
+
} | {
|
|
14
|
+
type: "rename";
|
|
15
|
+
session: OmbSessionInfo;
|
|
16
|
+
} | {
|
|
17
|
+
type: "delete";
|
|
18
|
+
session: OmbSessionInfo;
|
|
19
|
+
} | {
|
|
20
|
+
type: "settings";
|
|
21
|
+
settings: OmbSettings;
|
|
22
|
+
};
|
|
23
|
+
export declare function runInkAgentsHub(cwd: string, env: NodeJS.ProcessEnv, sessions: OmbSessionInfo[], settings: OmbSettings): Promise<AgentsHubAction>;
|
|
24
|
+
//# sourceMappingURL=agents-ink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents-ink.d.ts","sourceRoot":"","sources":["../../src/cli/agents-ink.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,cAAc,EAAwB,MAAM,wBAAwB,CAAC;AAC3H,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAG1D,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,WAAW,CAAA;CAAE,CAAC;AAyehD,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAOtJ"}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { Box, Text, useApp, useInput, useWindowSize, render } from "ink";
|
|
4
|
+
import { listOmbSessions, ombSessionDisplayName } from "../omp/omb-sessions.js";
|
|
5
|
+
import { filterDirectoryOptions, readDirectoryOptions } from "./directory-selector.js";
|
|
6
|
+
const statusLabel = (state) => state === "attached" ? "CONNECTED" : state === "detached" ? "DISCONNECTED" : "STOPPED";
|
|
7
|
+
const statusColor = (state) => state === "attached" ? "green" : state === "detached" ? "yellow" : "gray";
|
|
8
|
+
const statusMark = (state) => state === "attached" ? "●" : state === "detached" ? "◌" : "○";
|
|
9
|
+
const short = (value, width) => value.length <= width ? value : width <= 1 ? value.slice(0, width) : `${value.slice(0, width - 1)}…`;
|
|
10
|
+
function age(session) {
|
|
11
|
+
const timestamp = session.lastAttachedAt ?? session.createdAt;
|
|
12
|
+
if (!timestamp)
|
|
13
|
+
return "age unknown";
|
|
14
|
+
const seconds = Math.max(0, Math.floor(Date.now() / 1000) - timestamp);
|
|
15
|
+
if (seconds < 60)
|
|
16
|
+
return "just now";
|
|
17
|
+
if (seconds < 3600)
|
|
18
|
+
return `${Math.floor(seconds / 60)}m ago`;
|
|
19
|
+
if (seconds < 86400)
|
|
20
|
+
return `${Math.floor(seconds / 3600)}h ago`;
|
|
21
|
+
return `${Math.floor(seconds / 86400)}d ago`;
|
|
22
|
+
}
|
|
23
|
+
function projectCounts(sessions) {
|
|
24
|
+
const counts = new Map();
|
|
25
|
+
for (const session of sessions)
|
|
26
|
+
counts.set(session.project, (counts.get(session.project) ?? 0) + 1);
|
|
27
|
+
return [...counts.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
28
|
+
}
|
|
29
|
+
function visibleSessions(allSessions, settings) {
|
|
30
|
+
return settings.showStoppedSessions ? allSessions : allSessions.filter(session => session.state !== "dead");
|
|
31
|
+
}
|
|
32
|
+
function Header({ cwd, sessions }) {
|
|
33
|
+
const connected = sessions.filter(session => session.state === "attached").length;
|
|
34
|
+
const disconnected = sessions.filter(session => session.state === "detached").length;
|
|
35
|
+
const stopped = sessions.filter(session => session.state === "dead").length;
|
|
36
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { bold: true, color: "cyan", children: "\u25C6 BIZAR / AGENTS HUB" }), _jsx(Text, { dimColor: true, children: short(cwd, 44) })] }), _jsxs(Box, { marginTop: 1, gap: 2, children: [_jsxs(Text, { color: "green", children: ["\u25CF ", connected, " connected"] }), _jsxs(Text, { color: "yellow", children: ["\u25CC ", disconnected, " disconnected"] }), _jsxs(Text, { color: "gray", children: ["\u25CB ", stopped, " stopped"] })] })] }));
|
|
37
|
+
}
|
|
38
|
+
function WorkspaceRail({ sessions, selected }) {
|
|
39
|
+
const projects = projectCounts(sessions);
|
|
40
|
+
return (_jsxs(Box, { width: "28%", minWidth: 24, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "WORKSPACES" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { color: "white", bold: true, children: ["\u203A ALL SESSIONS ", _jsx(Text, { dimColor: true, children: sessions.length })] }), projects.length === 0 ? _jsx(Text, { dimColor: true, children: " No active projects" }) : projects.map(([project, count]) => (_jsxs(Text, { color: selected?.project === project ? "cyan" : undefined, children: [selected?.project === project ? " › " : " ", short(project, 18), " ", _jsx(Text, { dimColor: true, children: count })] }, project)))] }), _jsxs(Box, { marginTop: 2, flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { bold: true, color: "yellow", children: "LIVE VIEW" }), _jsx(Text, { dimColor: true, children: "Updates every few seconds." }), _jsx(Text, { dimColor: true, children: "Only bizar-* sessions" }), _jsx(Text, { dimColor: true, children: "are shown or changed." })] })] }));
|
|
41
|
+
}
|
|
42
|
+
function SessionRow({ session, selected, width }) {
|
|
43
|
+
const color = statusColor(session.state);
|
|
44
|
+
const displayName = ombSessionDisplayName(session);
|
|
45
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, backgroundColor: selected ? "blue" : undefined, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { color: selected ? "white" : color, bold: selected, children: [selected ? "›" : " ", " ", statusMark(session.state), " ", short(displayName, Math.max(18, width - 28))] }), _jsx(Text, { color: selected ? "white" : color, dimColor: !selected, children: statusLabel(session.state) })] }), _jsxs(Box, { paddingLeft: 4, justifyContent: "space-between", children: [_jsx(Text, { dimColor: true, children: short(`${session.project}/${session.id} · ${session.cwd}`, Math.max(20, width - 20)) }), _jsx(Text, { dimColor: true, children: age(session) })] })] }));
|
|
46
|
+
}
|
|
47
|
+
function SessionList({ sessions, selected, rows, width }) {
|
|
48
|
+
const visibleCount = Math.max(4, rows - 7);
|
|
49
|
+
const start = Math.max(0, Math.min(Math.max(0, sessions.length - visibleCount), selected - Math.floor(visibleCount / 2)));
|
|
50
|
+
const visible = sessions.slice(start, start + visibleCount);
|
|
51
|
+
let previous;
|
|
52
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { bold: true, color: "cyan", children: "SESSIONS" }), _jsx(Text, { dimColor: true, children: sessions.length ? `${start + 1}-${Math.min(start + visibleCount, sessions.length)} of ${sessions.length}` : "empty" })] }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: visible.length === 0 ? (_jsxs(Box, { flexDirection: "column", paddingY: 2, children: [_jsx(Text, { color: "yellow", children: "No Bizar sessions are running." }), _jsx(Text, { dimColor: true, children: "Press n to dispatch a new agent session." })] })) : visible.map((session, offset) => {
|
|
53
|
+
const index = start + offset;
|
|
54
|
+
const heading = session.state !== previous;
|
|
55
|
+
previous = session.state;
|
|
56
|
+
return (_jsxs(Box, { flexDirection: "column", children: [heading && _jsxs(Text, { color: statusColor(session.state), bold: true, children: [statusMark(session.state), " ", statusLabel(session.state)] }), _jsx(SessionRow, { session: session, selected: index === selected, width: width })] }, session.name));
|
|
57
|
+
}) })] }));
|
|
58
|
+
}
|
|
59
|
+
function Inspector({ session }) {
|
|
60
|
+
return (_jsxs(Box, { width: "29%", minWidth: 25, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "SELECTED SESSION" }), !session ? _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Select a session to inspect it." }) }) : (_jsxs(Box, { marginTop: 1, flexDirection: "column", gap: 1, children: [_jsxs(Text, { bold: true, color: statusColor(session.state), children: [statusMark(session.state), " ", statusLabel(session.state)] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "NAME" }), _jsx(Text, { children: ombSessionDisplayName(session) }), _jsx(Text, { dimColor: true, children: session.nativeName ? "OMP session title" : "Generated from task or project" })] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "PROJECT" }), _jsx(Text, { children: short(session.cwd, 30) })] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "COMMAND" }), _jsx(Text, { children: short(session.command || "omp", 30) })] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "LAST ACTIVITY" }), _jsx(Text, { children: age(session) })] })] }))] }));
|
|
61
|
+
}
|
|
62
|
+
function Footer() {
|
|
63
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, justifyContent: "space-between", children: [_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "Enter" }), " attach ", _jsx(Text, { color: "cyan", children: "n" }), " new selector ", _jsx(Text, { color: "cyan", children: "e" }), " rename ", _jsx(Text, { color: "cyan", children: "d" }), " delete"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "s" }), " settings ", _jsx(Text, { color: "cyan", children: "r" }), " refresh ", _jsx(Text, { color: "cyan", children: "?" }), " help ", _jsx(Text, { color: "cyan", children: "q" }), " quit"] })] }));
|
|
64
|
+
}
|
|
65
|
+
function Modal({ title, children, width, columns }) {
|
|
66
|
+
return (_jsxs(Box, { position: "absolute", top: 2, left: Math.max(0, Math.floor((columns - width) / 2)), width: width, flexDirection: "column", borderStyle: "double", borderColor: "cyan", backgroundColor: "black", paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: title }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: children })] }));
|
|
67
|
+
}
|
|
68
|
+
function NewSessionModal({ cwd, sessions, selected, columns, rows, onSubmit, onClose, }) {
|
|
69
|
+
const initialDirectory = selected?.cwd && selected.cwd !== "?" ? selected.cwd : cwd;
|
|
70
|
+
const [mode, setMode] = useState("directory");
|
|
71
|
+
const [directoryPath, setDirectoryPath] = useState(initialDirectory);
|
|
72
|
+
const [directoryQuery, setDirectoryQuery] = useState("");
|
|
73
|
+
const [directoryOptions, setDirectoryOptions] = useState([]);
|
|
74
|
+
const [directorySelected, setDirectorySelected] = useState(0);
|
|
75
|
+
const [projectCwd, setProjectCwd] = useState(initialDirectory);
|
|
76
|
+
const [task, setTask] = useState("");
|
|
77
|
+
const [sessionId, setSessionId] = useState("");
|
|
78
|
+
const [loading, setLoading] = useState(true);
|
|
79
|
+
const recentPaths = useMemo(() => [...new Set(sessions.map(session => session.cwd).filter(path => path && path !== "?"))], [sessions]);
|
|
80
|
+
const filteredDirectories = useMemo(() => filterDirectoryOptions(directoryOptions, directoryQuery), [directoryOptions, directoryQuery]);
|
|
81
|
+
const visibleCount = Math.max(4, Math.min(9, rows - 14));
|
|
82
|
+
const directoryStart = Math.max(0, Math.min(Math.max(0, filteredDirectories.length - visibleCount), directorySelected - Math.floor(visibleCount / 2)));
|
|
83
|
+
const visibleDirectories = filteredDirectories.slice(directoryStart, directoryStart + visibleCount);
|
|
84
|
+
const selectedDirectory = filteredDirectories[directorySelected];
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
let active = true;
|
|
87
|
+
setLoading(true);
|
|
88
|
+
void readDirectoryOptions(directoryPath, recentPaths, directoryQuery).then(options => {
|
|
89
|
+
if (!active)
|
|
90
|
+
return;
|
|
91
|
+
setDirectoryOptions(options);
|
|
92
|
+
setDirectorySelected(0);
|
|
93
|
+
setLoading(false);
|
|
94
|
+
});
|
|
95
|
+
return () => { active = false; };
|
|
96
|
+
}, [directoryPath, directoryQuery, recentPaths]);
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
setDirectorySelected(value => Math.min(value, Math.max(0, filteredDirectories.length - 1)));
|
|
99
|
+
}, [filteredDirectories.length]);
|
|
100
|
+
const selectDirectory = (option) => {
|
|
101
|
+
if (!option)
|
|
102
|
+
return;
|
|
103
|
+
if (option.kind === "parent") {
|
|
104
|
+
setDirectoryPath(option.path);
|
|
105
|
+
setDirectoryQuery("");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
setProjectCwd(option.path);
|
|
109
|
+
setMode("task");
|
|
110
|
+
};
|
|
111
|
+
const openDirectory = (option) => {
|
|
112
|
+
if (!option || option.kind === "current")
|
|
113
|
+
return;
|
|
114
|
+
setDirectoryPath(option.path);
|
|
115
|
+
setDirectoryQuery("");
|
|
116
|
+
};
|
|
117
|
+
const goToParent = () => {
|
|
118
|
+
const parent = directoryPath.replace(/[\\/]$/, "").split(/[\\/]/).slice(0, -1).join("/") || "/";
|
|
119
|
+
if (parent !== directoryPath) {
|
|
120
|
+
setDirectoryPath(parent);
|
|
121
|
+
setDirectoryQuery("");
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
useInput((input, key) => {
|
|
125
|
+
if (key.escape) {
|
|
126
|
+
if (mode === "name")
|
|
127
|
+
setMode("task");
|
|
128
|
+
else if (mode === "task")
|
|
129
|
+
setMode("directory");
|
|
130
|
+
else
|
|
131
|
+
onClose();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (mode === "directory") {
|
|
135
|
+
if (key.upArrow || input === "k")
|
|
136
|
+
setDirectorySelected(value => Math.max(0, value - 1));
|
|
137
|
+
else if (key.downArrow || input === "j")
|
|
138
|
+
setDirectorySelected(value => Math.min(Math.max(0, filteredDirectories.length - 1), value + 1));
|
|
139
|
+
else if (key.return)
|
|
140
|
+
selectDirectory(selectedDirectory);
|
|
141
|
+
else if (key.rightArrow || input === "l")
|
|
142
|
+
openDirectory(selectedDirectory);
|
|
143
|
+
else if (key.leftArrow || input === "h")
|
|
144
|
+
goToParent();
|
|
145
|
+
else if (key.backspace || (key.ctrl && input === "h")) {
|
|
146
|
+
setDirectoryQuery(value => value.slice(0, -1));
|
|
147
|
+
setDirectorySelected(0);
|
|
148
|
+
}
|
|
149
|
+
else if (key.ctrl && input === "u") {
|
|
150
|
+
setDirectoryQuery("");
|
|
151
|
+
setDirectorySelected(0);
|
|
152
|
+
}
|
|
153
|
+
else if (input && !key.ctrl && !key.meta && input >= " ") {
|
|
154
|
+
setDirectoryQuery(value => `${value}${input}`);
|
|
155
|
+
setDirectorySelected(0);
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const updateText = (setter) => {
|
|
160
|
+
if (key.backspace || (key.ctrl && input === "h"))
|
|
161
|
+
setter(value => value.slice(0, -1));
|
|
162
|
+
else if (key.ctrl && input === "u")
|
|
163
|
+
setter("");
|
|
164
|
+
else if (input && !key.ctrl && !key.meta && input >= " ")
|
|
165
|
+
setter(value => `${value}${input}`);
|
|
166
|
+
};
|
|
167
|
+
if (mode === "task") {
|
|
168
|
+
if (key.return)
|
|
169
|
+
setMode("name");
|
|
170
|
+
else
|
|
171
|
+
updateText(setTask);
|
|
172
|
+
}
|
|
173
|
+
else if (key.return) {
|
|
174
|
+
onSubmit({ projectCwd, task: task.trim(), sessionId: sessionId.trim() });
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
updateText(setSessionId);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
const width = Math.min(92, Math.max(54, columns - 6));
|
|
181
|
+
return (_jsx(Modal, { title: "NEW BIZAR SESSION", width: width, columns: columns, children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: mode === "directory" ? "cyan" : undefined, bold: mode === "directory", children: "1 Project directory" }), mode === "directory" ? (_jsxs(_Fragment, { children: [_jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "cyan", children: "\u2315 " }), _jsx(Text, { children: directoryQuery || "Type to fuzzy-search directories" })] }), _jsx(Text, { dimColor: true, children: directoryPath }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: loading ? _jsx(Text, { color: "yellow", children: "Scanning directories\u2026" }) : visibleDirectories.length === 0 ? _jsx(Text, { color: "yellow", children: "No matching directories. Type an absolute path to jump." }) : visibleDirectories.map((option, offset) => {
|
|
182
|
+
const index = directoryStart + offset;
|
|
183
|
+
const isSelected = index === directorySelected;
|
|
184
|
+
return (_jsxs(Box, { flexDirection: "column", backgroundColor: isSelected ? "blue" : undefined, children: [_jsxs(Text, { color: isSelected ? "white" : option.kind === "recent" ? "green" : "cyan", bold: isSelected, children: [isSelected ? "›" : " ", " ", option.kind === "parent" ? "↰" : option.kind === "recent" ? "●" : option.kind === "current" ? "◆" : "□", " ", option.label] }), isSelected && _jsxs(Text, { dimColor: true, children: [" ", option.detail ?? option.path] })] }, `${option.kind}:${option.path}`));
|
|
185
|
+
}) }), _jsxs(Text, { dimColor: true, children: [filteredDirectories.length ? `${directoryStart + 1}-${Math.min(directoryStart + visibleCount, filteredDirectories.length)} of ${filteredDirectories.length}` : "0 directories", " \u00B7 Enter choose \u2192 open \u2190 parent"] })] })) : (_jsxs(Text, { dimColor: true, children: [" ", projectCwd] })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: mode === "task" ? "cyan" : undefined, bold: mode === "task", children: "2 Initial task" }), mode === "task" ? _jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { color: "cyan", children: "\u203A " }), _jsx(Text, { children: task || "Optional task description" })] }) : _jsxs(Text, { dimColor: true, children: [" ", task || "Optional"] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: mode === "name" ? "cyan" : undefined, bold: mode === "name", children: "3 Session name" }), mode === "name" ? _jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { color: "cyan", children: "\u203A " }), _jsx(Text, { children: sessionId || "Blank generates a name" })] }) : _jsxs(Text, { dimColor: true, children: [" ", sessionId || "Generated automatically"] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "Type to search or edit \u00B7 Up/Down select \u00B7 Enter continue \u00B7 Esc back" }), _jsx(Text, { color: "yellow", children: mode === "directory" ? "Recent project directories are shown above the current folder." : mode === "name" ? "Enter starts the durable OMB host." : "Enter moves to the session name." })] })] }) }));
|
|
186
|
+
}
|
|
187
|
+
function SettingsModal({ settings, selected, columns, onToggle, onSave, onClose }) {
|
|
188
|
+
const options = [
|
|
189
|
+
["Attach plain omb to latest live session", settings.autoAttachLatest],
|
|
190
|
+
["Enable mouse scrolling in OMB sessions", settings.mouseScrolling],
|
|
191
|
+
["Show stopped sessions in this view", settings.showStoppedSessions],
|
|
192
|
+
];
|
|
193
|
+
return (_jsxs(Modal, { title: "OMB SETTINGS", width: Math.min(70, Math.max(46, columns - 6)), columns: columns, children: [options.map(([label, enabled], index) => (_jsxs(Text, { color: index === selected ? "white" : undefined, backgroundColor: index === selected ? "blue" : undefined, children: [index === selected ? "›" : " ", " [", enabled ? "on " : "off", "] ", label] }, label))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Up/Down select Space/Enter toggle s save Esc cancel" }), _jsx(Text, { color: "yellow", children: selected === 1 ? "Mouse mode applies to existing OMB sessions when saved." : "" }), _jsx(Text, { children: selected >= 0 ? "" : "" }), _jsx(SettingsKeyHandler, { onToggle: onToggle, onSave: onSave, onClose: onClose })] }));
|
|
194
|
+
}
|
|
195
|
+
function SettingsKeyHandler({ onToggle, onSave, onClose }) {
|
|
196
|
+
useInput((input, key) => {
|
|
197
|
+
if (key.upArrow || key.downArrow)
|
|
198
|
+
return;
|
|
199
|
+
if (key.escape)
|
|
200
|
+
onClose();
|
|
201
|
+
else if (input === " " || key.return)
|
|
202
|
+
onToggle();
|
|
203
|
+
else if (input === "s")
|
|
204
|
+
onSave();
|
|
205
|
+
});
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
function HelpModal({ columns, onClose }) {
|
|
209
|
+
useInput((input, key) => { if (key.escape || key.return || input === "q")
|
|
210
|
+
onClose(); });
|
|
211
|
+
return (_jsxs(Modal, { title: "OMB AGENTS SHORTCUTS", width: Math.min(64, Math.max(42, columns - 6)), columns: columns, children: [_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "Enter" }), " attach to the selected session"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "n" }), " open the fuzzy directory session selector"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "e" }), " rename the selected session"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "d" }), " delete after confirmation"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "s" }), " open launcher settings"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "r" }), " refresh the session list"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "q / Esc" }), " leave this view"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Only tmux sessions with the bizar- prefix are managed." }), _jsx(Text, { dimColor: true, children: "Press Enter or Esc to return." })] }));
|
|
212
|
+
}
|
|
213
|
+
function DeleteModal({ session, columns, onConfirm, onClose }) {
|
|
214
|
+
useInput((input, key) => {
|
|
215
|
+
if (input.toLowerCase() === "y")
|
|
216
|
+
onConfirm();
|
|
217
|
+
else if (key.escape || key.return || input.length > 0)
|
|
218
|
+
onClose();
|
|
219
|
+
});
|
|
220
|
+
return (_jsxs(Modal, { title: "DELETE SESSION?", width: Math.min(64, Math.max(42, columns - 6)), columns: columns, children: [_jsxs(Text, { bold: true, children: [session.project, "/", session.id] }), _jsx(Text, { dimColor: true, children: short(session.cwd, 54) }), _jsx(Text, { children: " " }), _jsx(Text, { color: "yellow", children: "This stops OMP and removes the tmux session." }), _jsxs(Text, { children: ["Press ", _jsx(Text, { color: "red", bold: true, children: "y" }), " to delete, any other key to cancel."] })] }));
|
|
221
|
+
}
|
|
222
|
+
function AgentsApp({ cwd, env, sessions: initialSessions, settings: initialSettings, onAction }) {
|
|
223
|
+
const { columns, rows } = useWindowSize();
|
|
224
|
+
const [allSessions, setAllSessions] = useState(initialSessions);
|
|
225
|
+
const [settings, setSettings] = useState(initialSettings);
|
|
226
|
+
const [selected, setSelected] = useState(0);
|
|
227
|
+
const [overlay, setOverlay] = useState();
|
|
228
|
+
const [overlaySelected, setOverlaySelected] = useState(0);
|
|
229
|
+
const [draftSettings, setDraftSettings] = useState(initialSettings);
|
|
230
|
+
const { exit } = useApp();
|
|
231
|
+
const sessions = useMemo(() => visibleSessions(allSessions, settings), [allSessions, settings]);
|
|
232
|
+
const selectedSession = sessions[selected];
|
|
233
|
+
const refresh = async () => setAllSessions(await listOmbSessions(cwd, env));
|
|
234
|
+
useEffect(() => {
|
|
235
|
+
const timer = setInterval(() => { void refresh(); }, 3000);
|
|
236
|
+
return () => clearInterval(timer);
|
|
237
|
+
}, [cwd, env]);
|
|
238
|
+
useEffect(() => { setSelected(value => Math.min(value, Math.max(0, sessions.length - 1))); }, [sessions.length]);
|
|
239
|
+
const finish = (action) => { onAction(action); exit(); };
|
|
240
|
+
useInput((input, key) => {
|
|
241
|
+
if (overlay)
|
|
242
|
+
return;
|
|
243
|
+
if (key.ctrl && input === "c") {
|
|
244
|
+
finish({ type: "quit" });
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (key.escape || input === "q") {
|
|
248
|
+
finish({ type: "quit" });
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (key.upArrow || input === "k")
|
|
252
|
+
setSelected(value => Math.max(0, value - 1));
|
|
253
|
+
else if (key.downArrow || input === "j")
|
|
254
|
+
setSelected(value => Math.min(Math.max(0, sessions.length - 1), value + 1));
|
|
255
|
+
else if (key.return && selectedSession)
|
|
256
|
+
finish({ type: "attach", session: selectedSession });
|
|
257
|
+
else if (input === "n")
|
|
258
|
+
setOverlay("new");
|
|
259
|
+
else if (input === "e" && selectedSession)
|
|
260
|
+
finish({ type: "rename", session: selectedSession });
|
|
261
|
+
else if (input === "d" && selectedSession) {
|
|
262
|
+
setOverlay("delete");
|
|
263
|
+
}
|
|
264
|
+
else if (input === "r") {
|
|
265
|
+
void refresh();
|
|
266
|
+
}
|
|
267
|
+
else if (input === "?")
|
|
268
|
+
setOverlay("help");
|
|
269
|
+
else if (input === "s") {
|
|
270
|
+
setDraftSettings(settings);
|
|
271
|
+
setOverlaySelected(0);
|
|
272
|
+
setOverlay("settings");
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
return (_jsxs(Box, { flexDirection: "column", width: "100%", minHeight: Math.max(12, rows - 1), paddingX: 1, children: [_jsx(Header, { cwd: cwd, sessions: sessions }), _jsxs(Box, { flexGrow: 1, marginTop: 1, gap: 1, children: [_jsx(WorkspaceRail, { sessions: sessions, selected: selectedSession }), _jsx(SessionList, { sessions: sessions, selected: selected, rows: rows, width: Math.max(34, Math.floor(columns * 0.42)) }), _jsx(Inspector, { session: selectedSession })] }), _jsx(Footer, {}), overlay === "new" && (_jsx(NewSessionModal, { cwd: cwd, sessions: allSessions, selected: selectedSession, columns: columns, rows: rows, onSubmit: value => finish({ type: "new", ...value }), onClose: () => setOverlay(undefined) })), overlay === "help" && _jsx(HelpModal, { columns: columns, onClose: () => setOverlay(undefined) }), overlay === "delete" && selectedSession && _jsx(DeleteModal, { session: selectedSession, columns: columns, onConfirm: () => finish({ type: "delete", session: selectedSession }), onClose: () => setOverlay(undefined) }), overlay === "settings" && (_jsx(SettingsModal, { settings: draftSettings, selected: overlaySelected, columns: columns, onToggle: () => {
|
|
276
|
+
const field = overlaySelected === 0 ? "autoAttachLatest" : overlaySelected === 1 ? "mouseScrolling" : "showStoppedSessions";
|
|
277
|
+
setDraftSettings(value => ({ ...value, [field]: !value[field] }));
|
|
278
|
+
}, onSave: () => finish({ type: "settings", settings: draftSettings }), onClose: () => setOverlay(undefined) })), overlay === "settings" && _jsx(SettingsNavigation, { selected: overlaySelected, onChange: setOverlaySelected })] }));
|
|
279
|
+
}
|
|
280
|
+
function SettingsNavigation({ selected, onChange }) {
|
|
281
|
+
useInput((_input, key) => {
|
|
282
|
+
if (key.upArrow)
|
|
283
|
+
onChange(Math.max(0, selected - 1));
|
|
284
|
+
else if (key.downArrow)
|
|
285
|
+
onChange(Math.min(2, selected + 1));
|
|
286
|
+
});
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
export async function runInkAgentsHub(cwd, env, sessions, settings) {
|
|
290
|
+
let action = { type: "quit" };
|
|
291
|
+
const app = render(_jsx(AgentsApp, { cwd: cwd, env: env, sessions: sessions, settings: settings, onAction: value => { action = value; } }), {
|
|
292
|
+
alternateScreen: true,
|
|
293
|
+
});
|
|
294
|
+
await app.waitUntilExit();
|
|
295
|
+
return action;
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=agents-ink.js.map
|