rtrt-agent 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +45 -10
- package/README.md +45 -10
- package/THIRD_PARTY_NOTICES/INDEX.md +24 -11
- package/THIRD_PARTY_NOTICES/option-ext@0.2.0/SOURCE.crate +0 -0
- package/package.json +43 -12
- package/server.js +98 -0
- package/tui/index.js +1 -0
- package/tui/rtrt-statusline-core.d.mts +205 -0
- package/tui/rtrt-statusline-core.mjs +1145 -0
- package/tui/v2/native-runtime.mjs +81 -0
- package/tui/v2/native-state.mjs +66 -0
- package/tui/v2/rtrt-statusline.tsx +116 -0
- package/v2/permissions.js +119 -0
package/README.ko.md
CHANGED
|
@@ -4,23 +4,25 @@
|
|
|
4
4
|
agent, worktree, invocation 및 permission broker 문맥을 보존하고 로컬 대시보드를
|
|
5
5
|
백그라운드에서 사용할 수 있게 유지합니다.
|
|
6
6
|
|
|
7
|
-
## 설치
|
|
7
|
+
## 설치 (v1 setup-managed wiring, v0.2.0)
|
|
8
|
+
|
|
9
|
+
이 소스는 v0.2.0을 대상으로 합니다. 설치하려면 버전이 일치하는 게시된 npm release가 필요하며, 소스의 버전 표시는 게시 증명이 아닙니다. 이전 `rtrt-agent@0.1.7` release는 그대로이며 아래 native v2 진입점을 포함하지 않습니다.
|
|
10
|
+
|
|
11
|
+
버전이 일치하는 npm release의 설치 형태는 다음과 같습니다.
|
|
8
12
|
|
|
9
13
|
```sh
|
|
10
|
-
npm install rtrt-agent@0.
|
|
14
|
+
npm install rtrt-agent@0.2.0
|
|
11
15
|
```
|
|
12
16
|
|
|
13
17
|
`opencode.json`의 단수형 `plugin` 배열에 패키지를 등록합니다.
|
|
14
18
|
|
|
15
19
|
```json
|
|
16
20
|
{
|
|
17
|
-
"plugin": ["rtrt-agent@0.
|
|
21
|
+
"plugin": ["rtrt-agent@0.2.0"]
|
|
18
22
|
}
|
|
19
23
|
```
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
default export를 제공하지 않습니다. 선택 사항인 RTRT TUI statusline은 별도 소스
|
|
23
|
-
통합으로 유지되며 이 패키지에서 export하거나 pack하지 않습니다.
|
|
25
|
+
v1 패키지 root는 이름 있는 export `RtrtProvenance` 하나를 그대로 유지하며 v0.2.0에서도 이름은 바뀌지 않습니다. 선택 사항인 v1 TUI statusline은 별도 setup-managed 통합으로 남고 v1 패키지는 이를 export하거나 pack하지 않습니다. 새로 staged 된 native v2 소스는 아래에서 설명하며 `rtrt-agent@0.1.7`에는 게시되지 않습니다.
|
|
24
26
|
|
|
25
27
|
## 대시보드
|
|
26
28
|
|
|
@@ -44,20 +46,53 @@ state가 안전하지 않거나 없으면 시작만 fail-soft로 중단되고 Op
|
|
|
44
46
|
|
|
45
47
|
`rtrt setup --agent opencode --apply`는 전체 RTRT 통합을 관리하지만 npm 설치는 수행하지
|
|
46
48
|
않으며, 설정된 패키지는 OpenCode가 시작할 때 설치합니다. Setup은 먼저 정확한
|
|
47
|
-
`rtrt-agent@0.
|
|
49
|
+
`rtrt-agent@0.2.0` 등록과 모든 대체 관리 asset을 기록합니다. 이 기록이 성공한 뒤에만
|
|
48
50
|
인식된 legacy RTRT plugin을 제거하며, 이전 단계가 실패하면 legacy runtime을 보존합니다.
|
|
49
51
|
Setup은 `rtrt-agent`의 정확히 일치하는 bare, pinned, ranged, tuple, object 형식만 소유하고
|
|
50
|
-
정확한 `"rtrt-agent@0.
|
|
52
|
+
정확한 `"rtrt-agent@0.2.0"` string 하나로 정규화합니다. 이전의 미게시 `rtrt`와 초안
|
|
51
53
|
`rtrt-opencode` 패키지 spec은 외부 값이며 순서를 유지합니다. 위 직접 등록과
|
|
52
|
-
`opencode plugin rtrt-agent@0.
|
|
54
|
+
`opencode plugin rtrt-agent@0.2.0 --global`은 계속 유효합니다.
|
|
53
55
|
|
|
54
56
|
관리 agent 소유권 상태는 다음 순서의 첫 비어 있지 않은 root에서 읽습니다.
|
|
55
57
|
`$OPENCODE_CONFIG_DIR`, `$XDG_CONFIG_HOME/opencode`, `~/.config/opencode`. 공존은 OMO
|
|
56
58
|
4.19.4를 대상으로 CI에서 검사하고 OpenCode 1.18.29에서 직접 검증했으며, 어느 쪽도
|
|
57
59
|
미래 버전 지원을 보장하지 않습니다. 통합 RTRT 릴리스 워크플로는 정확한 버전의
|
|
58
|
-
대시보드 플랫폼 패키지 5개를 `rtrt-agent@0.
|
|
60
|
+
대시보드 플랫폼 패키지 5개를 `rtrt-agent@0.2.0`보다 먼저 게시한 뒤 일치하는 Rust
|
|
59
61
|
릴리스 artifact를 게시합니다.
|
|
60
62
|
|
|
63
|
+
## Native OpenCode 2.0.20 진입점
|
|
64
|
+
|
|
65
|
+
v0.2.0 source manifest의 pack 목록에는 native v2 server와 TUI 파일이 추가되지만, npm registry의 `rtrt-agent@0.1.7`에는 **포함되지 않습니다**. 게시된 `rtrt-agent@0.1.7`은 native v2 진입점을 설치·등록하지 않으며, 과거 `0.1.7` 패키지 계약은 게시된 그대로 유지됩니다. v0.2.0 native 소스의 검증 대상은 정확히 `@opencode/cli@2.0.20`과 `@opencode/plugin@2.0.20`이며, 1.x SDK의 v2 preview가 아닙니다. 패키지 root는 v1 이름 있는 export `RtrtProvenance`를 유지합니다 (이름만, v0.2.0에서도 변경 없음).
|
|
66
|
+
|
|
67
|
+
v0.2.0 source pack에 staged 된 항목:
|
|
68
|
+
|
|
69
|
+
- `./server`의 native `Plugin.Definition` default export. 이름 있는 `createNativeServer` 팩토리가 `{ id: "rtrt-agent", setup }` 형태로 생성합니다. v1 `RtrtProvenance` 경로는 그대로 별도입니다.
|
|
70
|
+
- `package.json`의 `exports`에서 `./server`와 `./tui`에 사용하는 single-element 배열. OpenCode 1.18.33 legacy resolver는 배열을 무시하고 `main`인 `index.js`로 fallback 하며, native 2.0.20은 정상적으로 해석합니다.
|
|
71
|
+
- v1 TUI statusline은 여전히 별도의 `app_bottom` 등록입니다. v2 native `rtrt-statusline.tsx`는 `tui/v2/` 아래에 있고 2.x에서만 동작하며 v1 연결은 그대로입니다.
|
|
72
|
+
- Native `prompt.footer.status` 등록과 server·TUI hook의 cleanup.
|
|
73
|
+
|
|
74
|
+
Tool-arg provenance, 대시보드 자동 시작, 명시적 `rtrt_dashboard_open`은 모두 native server 뒤에 있습니다. Permission 정책은 fail-closed이며 명시적 `deny`는 v1 broker와 v2 evaluator 양쪽에서 그대로 보존됩니다. 외부 Claude CLI permission broker 지원과 per-call shell identity / provider-limit recovery는 native v2 build에서 **지원되지 않습니다**. 이는 2.0.20 public API에 해당 표면이 없기 때문이며 어떤 workaround로도 자동 승인되지 않습니다. 기본 deny 정책은 v1 broker에서와 동일합니다.
|
|
75
|
+
|
|
76
|
+
### 로컬 소스 checkout (npm 설치 없음)
|
|
77
|
+
|
|
78
|
+
로컬 checkout으로 개발할 때는 OpenCode의 native `plugins` config가 디렉터리 참조를 받습니다. 정확한 경로는 checkout에 따라 다르므로 형식만 보여 줍니다.
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"plugins": [
|
|
83
|
+
"/absolute/path/to/rtrt/plugins/opencode"
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
이 디렉터리 형식은 격리된 HOME/XDG 환경에서 실제 `@opencode/cli@2.0.20` host로 로컬 검증했습니다. 그 정확한 버전에서 `server.js`가 active이고 native TUI 탐색 결과는 `features.tui: true`이며 RTRT stdio MCP가 연결됩니다. TUI strict typecheck, 번들 빌드, 120열·40열 headless footer 렌더링도 통과합니다. 검증은 전체 interactive OpenCode TUI를 다루지 않으며 다른 OpenCode 버전을 보장하지 않습니다. 검증 대상은 정확히 테스트한 `@opencode/cli@2.0.20` host에 한정됩니다.
|
|
89
|
+
|
|
90
|
+
### Native MCP 형식 (정보 제공, OpenCode 2 공식 schema)
|
|
91
|
+
|
|
92
|
+
Native local MCP 형식은 `mcp.servers.rtrt.type: "local"`과 `["/absolute/path/to/rtrt-mcp", "--transport", "stdio"]` 같은 `command` 배열을 사용합니다. 옵션 이름은 `disabled`와 `codemode`이며 sandbox source config에서는 둘 다 `false`입니다. Release에 고정된 v1 등록은 `rtrt-agent@0.2.0`입니다. `rtrt setup --agent opencode --apply`는 여전히 v1 설정을 기록하며 native v2 설치가 아닙니다. Setup 자체는 npm 패키지를 설치하지 않고, OpenCode가 시작할 때 설정된 패키지를 해석합니다.
|
|
93
|
+
|
|
94
|
+
게시된 `rtrt-agent@0.1.7`은 npm registry에서 그대로입니다. Sandbox의 기본 v2 설정은 MCP-only이며 staged native plugin·footer·forwarding adapter는 그 경로에서 활성화되지 않습니다. 이 소스 작업은 Docker 배포, `rtrt collector` 서비스, 운영자 측 `forward` 배선 또는 다른 운영 서비스를 promote, 활성화, 연결하지 않습니다 — 이 표면은 분리되어 있고 v0.2.0 소스 변경으로 켜지지 않습니다.
|
|
95
|
+
|
|
61
96
|
## 라이선스
|
|
62
97
|
|
|
63
98
|
MIT
|
package/README.md
CHANGED
|
@@ -4,23 +4,25 @@
|
|
|
4
4
|
worktree, invocation, and permission-broker context across supported hooks and keeps
|
|
5
5
|
the local dashboard available in the background.
|
|
6
6
|
|
|
7
|
-
## Install
|
|
7
|
+
## Install (v1 setup-managed wiring, v0.2.0)
|
|
8
|
+
|
|
9
|
+
This source targets v0.2.0. Installation requires the matching published npm release; a source version marker is not proof of publication. The older `rtrt-agent@0.1.7` release remains unchanged and does not include the native v2 entries described below.
|
|
10
|
+
|
|
11
|
+
For the matching npm release:
|
|
8
12
|
|
|
9
13
|
```sh
|
|
10
|
-
npm install rtrt-agent@0.
|
|
14
|
+
npm install rtrt-agent@0.2.0
|
|
11
15
|
```
|
|
12
16
|
|
|
13
17
|
Register the package in the singular `plugin` array in `opencode.json`:
|
|
14
18
|
|
|
15
19
|
```json
|
|
16
20
|
{
|
|
17
|
-
"plugin": ["rtrt-agent@0.
|
|
21
|
+
"plugin": ["rtrt-agent@0.2.0"]
|
|
18
22
|
}
|
|
19
23
|
```
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
default export. The optional RTRT TUI statusline remains a separate source integration
|
|
23
|
-
and is not exported or packed by this package.
|
|
25
|
+
The v1 package root remains the named export `RtrtProvenance` only — its name is unchanged across the v0.2.0 source. The optional v1 TUI statusline stays a separate setup-managed integration and is not exported or packed by the v1 package. The newly staged native v2 source is described below and is not published in `rtrt-agent@0.1.7`.
|
|
24
26
|
|
|
25
27
|
## Dashboard
|
|
26
28
|
|
|
@@ -43,21 +45,54 @@ Existing `~/.rtrt` data is preserved.
|
|
|
43
45
|
|
|
44
46
|
`rtrt setup --agent opencode --apply` manages the full RTRT integration but performs no
|
|
45
47
|
npm installation; OpenCode installs the configured package at startup. Setup first writes
|
|
46
|
-
the exact `rtrt-agent@0.
|
|
48
|
+
the exact `rtrt-agent@0.2.0` registration and every replacement managed asset. Only after
|
|
47
49
|
those writes succeed does it remove a recognized legacy RTRT plugin; an earlier failure
|
|
48
50
|
preserves the legacy runtime. Setup owns exact bare, pinned, ranged, tuple, and object forms
|
|
49
|
-
of `rtrt-agent` only and normalizes them to one exact `"rtrt-agent@0.
|
|
51
|
+
of `rtrt-agent` only and normalizes them to one exact `"rtrt-agent@0.2.0"` string. Old
|
|
50
52
|
unpublished `rtrt` and draft `rtrt-opencode` package specs are foreign and retain their
|
|
51
|
-
order. The direct registration above and `opencode plugin rtrt-agent@0.
|
|
53
|
+
order. The direct registration above and `opencode plugin rtrt-agent@0.2.0 --global`
|
|
52
54
|
remain valid.
|
|
53
55
|
|
|
54
56
|
Managed-agent ownership state is read from the first nonempty root in this order:
|
|
55
57
|
`$OPENCODE_CONFIG_DIR`, `$XDG_CONFIG_HOME/opencode`, then `~/.config/opencode`.
|
|
56
58
|
Coexistence is CI-gated against OMO 4.19.4 and was verified on OpenCode 1.18.29; neither
|
|
57
59
|
is a future-version guarantee. The unified RTRT release workflow publishes the exact
|
|
58
|
-
five exact-version dashboard platform packages before `rtrt-agent@0.
|
|
60
|
+
five exact-version dashboard platform packages before `rtrt-agent@0.2.0`, then publishes
|
|
59
61
|
the matching Rust release artifacts.
|
|
60
62
|
|
|
63
|
+
## Native OpenCode 2.0.20 Entries
|
|
64
|
+
|
|
65
|
+
The v0.2.0 manifest includes native v2 server and TUI files in the pack inventory. The older `rtrt-agent@0.1.7` release does not include or register those entries and remains unchanged. The native source targets exactly `@opencode/cli@2.0.20` and `@opencode/plugin@2.0.20`, not the 1.x SDK's v2 preview. The package root remains the v1 named export `RtrtProvenance` (name-only, unchanged across v0.2.0).
|
|
66
|
+
|
|
67
|
+
What is staged in the v0.2.0 source pack:
|
|
68
|
+
|
|
69
|
+
- A native `Plugin.Definition` default export at `./server`, constructed by the named `createNativeServer` factory and exposing `{ id: "rtrt-agent", setup }`. The v1 `RtrtProvenance` path stays separate.
|
|
70
|
+
- Single-element arrays for `./server` and `./tui` in `package.json` `exports`. OpenCode 1.18.33's legacy resolver ignores those arrays and falls back to `main` `index.js`; native 2.0.20 resolves them normally.
|
|
71
|
+
- A v1 TUI statusline that remains a separate `app_bottom` registration. The v2 native `rtrt-statusline.tsx` lives under `tui/v2/` and only runs in 2.x; v1 wiring is unchanged.
|
|
72
|
+
- A native `prompt.footer.status` registration and cleanup for both server and TUI hooks.
|
|
73
|
+
|
|
74
|
+
Tool-arg provenance, dashboard auto-startup, and an explicit `rtrt_dashboard_open` all live behind the native server. Permission policies are fail-closed: explicit `deny` is preserved across the v1 broker and the v2 evaluator. External Claude CLI permission broker support and per-call shell identity / provider-limit recovery are **unsupported** in the native v2 build because those surfaces are not in the 2.0.20 public API; they are not auto-approved via any workaround. The deny-by-default policy is unchanged from the v1 broker.
|
|
75
|
+
|
|
76
|
+
### Local source checkout (no npm install)
|
|
77
|
+
|
|
78
|
+
For development against a local checkout, OpenCode's native `plugins` config accepts a directory reference. Exact paths depend on your checkout, so only the shape is shown:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"plugins": [
|
|
83
|
+
"/absolute/path/to/rtrt/plugins/opencode"
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
This directory form was verified locally with the actual `@opencode/cli@2.0.20` host in an isolated HOME/XDG environment. With that exact version: `server.js` is active, native TUI discovery reports `features.tui: true`, RTRT stdio MCP connects, strict TUI typechecking and bundling pass, and headless footer rendering at 120 and 40 columns passes. The verification does not cover the full interactive OpenCode TUI and is not a guarantee for other OpenCode versions; it is specific to the exact `@opencode/cli@2.0.20` host that was tested.
|
|
89
|
+
|
|
90
|
+
### Native MCP shape (informational, OpenCode 2 official schema)
|
|
91
|
+
|
|
92
|
+
The native local MCP shape uses `mcp.servers.rtrt.type: "local"` and a `command` array such as `["/absolute/path/to/rtrt-mcp", "--transport", "stdio"]`; the option names are `disabled` and `codemode`, both `false` in the sandbox source config. The release-pinned v1 registration is `rtrt-agent@0.2.0`. `rtrt setup --agent opencode --apply` still writes v1 configuration, not native v2 installation; setup itself does not install npm packages, and OpenCode resolves the configured package at startup.
|
|
93
|
+
|
|
94
|
+
Published `rtrt-agent@0.1.7` remains unchanged on the npm registry. The sandbox's default v2 configuration is MCP-only; the staged native plugin, footer, and forwarding adapter are not activated there. This source work does not promote, activate, or wire up a Docker deployment, the `rtrt collector` service, the operator-side `forward` plumbing, or any other operational services — those remain separate surfaces and are not enabled by the v0.2.0 source change.
|
|
95
|
+
|
|
61
96
|
## License
|
|
62
97
|
|
|
63
98
|
MIT
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# Version-pinned distribution notices
|
|
2
2
|
|
|
3
3
|
These are unmodified LICENSE files extracted from the specified public npm and
|
|
4
|
-
crates.io archives
|
|
5
|
-
|
|
6
|
-
the five
|
|
7
|
-
|
|
4
|
+
crates.io archives, plus the complete unmodified `option-ext@0.2.0` source crate.
|
|
5
|
+
`LICENSE` at the distribution root remains RTRT's own MIT license. This directory
|
|
6
|
+
accompanies the source, the five GitHub binary archives, the five dashboard
|
|
7
|
+
platform npm packages, and `rtrt-agent`. It is a targeted notice set, not an
|
|
8
|
+
exhaustive dependency inventory or a legal certification.
|
|
8
9
|
|
|
9
10
|
The five MIT JS bundles `cytoscape-fcose`, `cytoscape-cola`, `cose-base`,
|
|
10
11
|
`layout-base`, and `webcola` are embedded in the dashboard without their upstream
|
|
@@ -32,9 +33,11 @@ the matching `https://registry.npmjs.org/<name>/<version>` `dist.integrity`
|
|
|
32
33
|
| webpki-roots@1.0.7 | https://static.crates.io/crates/webpki-roots/webpki-roots-1.0.7.crate | `52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d` |
|
|
33
34
|
| option-ext@0.2.0 | https://static.crates.io/crates/option-ext/option-ext-0.2.0.crate | `04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d` |
|
|
34
35
|
|
|
35
|
-
Paths below are relative to this directory; SHA-256 is of the
|
|
36
|
-
|
|
37
|
-
the upstream path is
|
|
36
|
+
Paths below are relative to this directory; SHA-256 is of the distributed local
|
|
37
|
+
file. LICENSE rows hash extracted files, not their upstream archives. For npm,
|
|
38
|
+
the upstream path is `package/LICENSE`. For crates, the upstream license path
|
|
39
|
+
is `<name>-<version>/<path below>`. The `SOURCE.crate` row instead hashes the
|
|
40
|
+
unmodified complete upstream archive bytes (also listed in the archive table).
|
|
38
41
|
|
|
39
42
|
| Extracted file | SHA-256 |
|
|
40
43
|
|---|---|
|
|
@@ -54,10 +57,20 @@ the upstream path is `<name>-<version>/<path below>`.
|
|
|
54
57
|
| `webpki-roots@0.26.11/LICENSE` | `e271993808fec50ab29350b39539cdec611a9103f827e0aa26d61da70e2d33f8` |
|
|
55
58
|
| `webpki-roots@1.0.7/LICENSE` | `e271993808fec50ab29350b39539cdec611a9103f827e0aa26d61da70e2d33f8` |
|
|
56
59
|
| `option-ext@0.2.0/LICENSE.txt` | `66a3107d5ad6a058aab753eaac2047ccb2ed0e39465dd0fe5844da3e300d5172` |
|
|
60
|
+
| `option-ext@0.2.0/SOURCE.crate` | `04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d` |
|
|
57
61
|
|
|
58
62
|
`ring`'s top-level LICENSE explains its ISC/Apache-2.0 BoringSSL split and
|
|
59
63
|
points to the included once_cell subpart texts. The included fiat subpart
|
|
60
|
-
license is copied from the same crate. `option-ext` is MPL-2.0:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
license is copied from the same crate. `option-ext@0.2.0` is MPL-2.0: its
|
|
65
|
+
complete, unchanged upstream source archive is bundled at
|
|
66
|
+
`THIRD_PARTY_NOTICES/option-ext@0.2.0/SOURCE.crate` inside each of the five
|
|
67
|
+
GitHub binary archives, five dashboard platform npm packages, and the
|
|
68
|
+
`rtrt-agent` npm package. It is the same version and SHA-256 as `Cargo.lock`;
|
|
69
|
+
recipients need no network download. From the distribution root, run
|
|
70
|
+
`mkdir option-ext-source && tar -xzf THIRD_PARTY_NOTICES/option-ext@0.2.0/SOURCE.crate -C option-ext-source`
|
|
71
|
+
to read the original `option-ext-0.2.0/src/`, `Cargo.toml`, `Cargo.toml.orig`,
|
|
72
|
+
`README.md`, and `LICENSE.txt` beneath `option-ext-source/`. The URL above is
|
|
73
|
+
an additional upstream retrieval option, not the only source offer. This
|
|
74
|
+
source-code copy applies only to the MPL-covered `option-ext` component; it
|
|
75
|
+
does not relicense RTRT. Qualified counsel should assess any remaining
|
|
76
|
+
distribution obligations, including MPL-2.0 section 3.2.
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rtrt-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "RTRT provenance, permission-broker, and local dashboard plugin for OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
7
7
|
"exports": {
|
|
8
|
-
".": "./index.js"
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./server": [
|
|
10
|
+
"./server.js"
|
|
11
|
+
],
|
|
12
|
+
"./tui": [
|
|
13
|
+
"./tui/v2/rtrt-statusline.tsx"
|
|
14
|
+
]
|
|
9
15
|
},
|
|
10
16
|
"bin": {
|
|
11
17
|
"rtrt-dashboard-open": "./bin/rtrt-dashboard-open.js"
|
|
@@ -13,12 +19,20 @@
|
|
|
13
19
|
"files": [
|
|
14
20
|
"index.js",
|
|
15
21
|
"rtrt-provenance.js",
|
|
22
|
+
"server.js",
|
|
23
|
+
"v2/permissions.js",
|
|
16
24
|
"bin/rtrt-dashboard-open.js",
|
|
17
25
|
"runtime/dashboard-binary.js",
|
|
18
26
|
"runtime/dashboard-files.js",
|
|
19
27
|
"runtime/dashboard-plugin.js",
|
|
20
28
|
"runtime/dashboard-process.js",
|
|
21
29
|
"runtime/dashboard-supervisor.js",
|
|
30
|
+
"tui/index.js",
|
|
31
|
+
"tui/rtrt-statusline-core.mjs",
|
|
32
|
+
"tui/rtrt-statusline-core.d.mts",
|
|
33
|
+
"tui/v2/native-runtime.mjs",
|
|
34
|
+
"tui/v2/native-state.mjs",
|
|
35
|
+
"tui/v2/rtrt-statusline.tsx",
|
|
22
36
|
"README.md",
|
|
23
37
|
"README.ko.md",
|
|
24
38
|
"LICENSE",
|
|
@@ -38,12 +52,9 @@
|
|
|
38
52
|
"THIRD_PARTY_NOTICES/subtle@2.6.1/LICENSE",
|
|
39
53
|
"THIRD_PARTY_NOTICES/webpki-roots@0.26.11/LICENSE",
|
|
40
54
|
"THIRD_PARTY_NOTICES/webpki-roots@1.0.7/LICENSE",
|
|
41
|
-
"THIRD_PARTY_NOTICES/option-ext@0.2.0/LICENSE.txt"
|
|
55
|
+
"THIRD_PARTY_NOTICES/option-ext@0.2.0/LICENSE.txt",
|
|
56
|
+
"THIRD_PARTY_NOTICES/option-ext@0.2.0/SOURCE.crate"
|
|
42
57
|
],
|
|
43
|
-
"scripts": {
|
|
44
|
-
"test": "node --test rtrt-provenance.test.mjs dashboard-binary.test.mjs dashboard-open.test.mjs dashboard-supervisor.test.mjs package.test.mjs notices-package.test.mjs platform-package.test.mjs platform-publish.test.mjs tui/rtrt-statusline.test.mjs",
|
|
45
|
-
"sync:platforms": "node ../../packaging/npm/agent-contract.mjs --sync"
|
|
46
|
-
},
|
|
47
58
|
"engines": {
|
|
48
59
|
"node": ">=20"
|
|
49
60
|
},
|
|
@@ -67,11 +78,31 @@
|
|
|
67
78
|
"dependencies": {
|
|
68
79
|
"@opencode-ai/sdk": "1.15.13"
|
|
69
80
|
},
|
|
81
|
+
"peerDependencies": {
|
|
82
|
+
"@opencode/plugin": ">=1.18.33 <3",
|
|
83
|
+
"@opentui/core": ">=0.4.5",
|
|
84
|
+
"@opentui/solid": ">=0.4.5",
|
|
85
|
+
"solid-js": ">=1.9.0"
|
|
86
|
+
},
|
|
87
|
+
"peerDependenciesMeta": {
|
|
88
|
+
"@opencode/plugin": {
|
|
89
|
+
"optional": true
|
|
90
|
+
},
|
|
91
|
+
"@opentui/core": {
|
|
92
|
+
"optional": true
|
|
93
|
+
},
|
|
94
|
+
"@opentui/solid": {
|
|
95
|
+
"optional": true
|
|
96
|
+
},
|
|
97
|
+
"solid-js": {
|
|
98
|
+
"optional": true
|
|
99
|
+
}
|
|
100
|
+
},
|
|
70
101
|
"optionalDependencies": {
|
|
71
|
-
"rtrt-dashboard-linux-x64": "0.
|
|
72
|
-
"rtrt-dashboard-linux-arm64": "0.
|
|
73
|
-
"rtrt-dashboard-darwin-x64": "0.
|
|
74
|
-
"rtrt-dashboard-darwin-arm64": "0.
|
|
75
|
-
"rtrt-dashboard-win32-x64": "0.
|
|
102
|
+
"rtrt-dashboard-linux-x64": "0.2.0",
|
|
103
|
+
"rtrt-dashboard-linux-arm64": "0.2.0",
|
|
104
|
+
"rtrt-dashboard-darwin-x64": "0.2.0",
|
|
105
|
+
"rtrt-dashboard-darwin-arm64": "0.2.0",
|
|
106
|
+
"rtrt-dashboard-win32-x64": "0.2.0"
|
|
76
107
|
}
|
|
77
108
|
}
|
package/server.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
|
|
4
|
+
import { createDashboardSupervisor } from "./runtime/dashboard-supervisor.js"
|
|
5
|
+
import { createPermissionEvaluator } from "./v2/permissions.js"
|
|
6
|
+
|
|
7
|
+
const RTRT_TOOLS = new Set(["rtrt_agent_call", "rtrt_agent_route"])
|
|
8
|
+
const DASHBOARD_INPUT = { type: "object", properties: {}, additionalProperties: false }
|
|
9
|
+
|
|
10
|
+
function scheduleStartup(start) {
|
|
11
|
+
const timer = setTimeout(() => { void start() }, 0)
|
|
12
|
+
timer.unref?.()
|
|
13
|
+
return { cancel: () => clearTimeout(timer) }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// The native server contract is @opencode/plugin 2.0.20. The v1 root remains
|
|
17
|
+
// separate: v2 has no permission.create or shell call identity, so it cannot
|
|
18
|
+
// provide the legacy Claude permission broker or per-call shell provenance.
|
|
19
|
+
export function createNativeServer({ supervisor, schedule = scheduleStartup, managedAgentStatePath, report = (message) => process.emitWarning(message, "RTRT_NATIVE_PLUGIN") } = {}) {
|
|
20
|
+
return {
|
|
21
|
+
id: "rtrt-agent",
|
|
22
|
+
async setup(ctx) {
|
|
23
|
+
const dashboard = supervisor ?? createDashboardSupervisor()
|
|
24
|
+
const invocations = new Map()
|
|
25
|
+
const registrations = []
|
|
26
|
+
let disposed = false
|
|
27
|
+
let cleanupPromise
|
|
28
|
+
const notify = (message) => {
|
|
29
|
+
try { report(message) } catch {
|
|
30
|
+
process.emitWarning("RTRT native diagnostic unavailable.", "RTRT_NATIVE_PLUGIN")
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const startup = schedule(() => Promise.resolve()
|
|
34
|
+
.then(() => { if (!disposed) return dashboard.ensure() })
|
|
35
|
+
.catch(() => { if (!disposed) notify("RTRT dashboard unavailable.") }))
|
|
36
|
+
const dispose = () => {
|
|
37
|
+
if (cleanupPromise) return cleanupPromise
|
|
38
|
+
disposed = true
|
|
39
|
+
invocations.clear()
|
|
40
|
+
cleanupPromise = (async () => {
|
|
41
|
+
const failures = []
|
|
42
|
+
try { startup.cancel() } catch { failures.push(new Error("RTRT startup cancellation failed.")) }
|
|
43
|
+
const outcomes = await Promise.allSettled(registrations.reverse().map((registration) =>
|
|
44
|
+
Promise.resolve().then(() => registration.dispose())))
|
|
45
|
+
for (const [index, outcome] of outcomes.entries()) {
|
|
46
|
+
if (outcome.status === "rejected") failures.push(new Error(`RTRT registration ${index} disposal failed.`))
|
|
47
|
+
}
|
|
48
|
+
if (failures.length) throw new AggregateError(failures, "RTRT native cleanup failed.")
|
|
49
|
+
})()
|
|
50
|
+
return cleanupPromise
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
registrations.push(await ctx.tool.hook("execute.before", (event) => {
|
|
54
|
+
if (disposed || !RTRT_TOOLS.has(event.tool) || !event.input || typeof event.input !== "object" || Array.isArray(event.input)) return
|
|
55
|
+
const key = `${event.sessionID}\0${event.id}`
|
|
56
|
+
let invocationID = invocations.get(key)
|
|
57
|
+
if (!invocationID) {
|
|
58
|
+
invocationID = randomUUID()
|
|
59
|
+
invocations.set(key, invocationID)
|
|
60
|
+
}
|
|
61
|
+
Object.assign(event.input, {
|
|
62
|
+
invocation_id: invocationID,
|
|
63
|
+
parent_project: path.basename(ctx.location.project.canonical),
|
|
64
|
+
parent_session_id: event.sessionID,
|
|
65
|
+
parent_call_id: event.id,
|
|
66
|
+
caller_agent: event.agent,
|
|
67
|
+
parent_cwd: ctx.location.directory,
|
|
68
|
+
parent_worktree: ctx.location.project.canonical,
|
|
69
|
+
})
|
|
70
|
+
}))
|
|
71
|
+
registrations.push(await ctx.tool.hook("execute.after", (event) => {
|
|
72
|
+
if (!disposed && RTRT_TOOLS.has(event.tool)) invocations.delete(`${event.sessionID}\0${event.id}`)
|
|
73
|
+
}))
|
|
74
|
+
registrations.push(await ctx.tool.transform((editor) => {
|
|
75
|
+
if (disposed) return
|
|
76
|
+
editor.add({
|
|
77
|
+
name: "rtrt_dashboard_open",
|
|
78
|
+
description: "Open the RTRT dashboard in the operator's browser when explicitly requested.",
|
|
79
|
+
input: DASHBOARD_INPUT,
|
|
80
|
+
async execute() {
|
|
81
|
+
if (disposed) return { content: "Dashboard unavailable." }
|
|
82
|
+
const status = await dashboard.open().catch(() => "Dashboard unavailable.")
|
|
83
|
+
return { content: status === "Dashboard open requested." ? status : "Dashboard unavailable." }
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
}))
|
|
87
|
+
const evaluate = await createPermissionEvaluator(ctx, managedAgentStatePath, () => !disposed)
|
|
88
|
+
registrations.push(await ctx.permission.hook("evaluate", evaluate))
|
|
89
|
+
return dispose
|
|
90
|
+
} catch (error) {
|
|
91
|
+
try { await dispose() } catch { notify("RTRT native cleanup failed.") }
|
|
92
|
+
throw error
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default createNativeServer()
|
package/tui/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./v2/rtrt-statusline.tsx"
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
export type StatuslineTone =
|
|
2
|
+
| "accent"
|
|
3
|
+
| "good"
|
|
4
|
+
| "warn"
|
|
5
|
+
| "bad"
|
|
6
|
+
| "muted"
|
|
7
|
+
|
|
8
|
+
export type StatuslineSegment = {
|
|
9
|
+
id: string
|
|
10
|
+
text: string
|
|
11
|
+
compact: string | null
|
|
12
|
+
tone: StatuslineTone
|
|
13
|
+
priority: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type StatuslinePayload = {
|
|
17
|
+
version: 1
|
|
18
|
+
stale: boolean
|
|
19
|
+
degraded: string[]
|
|
20
|
+
segments: StatuslineSegment[]
|
|
21
|
+
}
|
|
22
|
+
export type StatuslineViewRole = "badge" | "label" | "value" | "separator"
|
|
23
|
+
export type StatuslineViewPart = {
|
|
24
|
+
id: string
|
|
25
|
+
text: string
|
|
26
|
+
role: StatuslineViewRole
|
|
27
|
+
tone: StatuslineTone
|
|
28
|
+
}
|
|
29
|
+
export type StatuslineViewRow = { id: string; parts: StatuslineViewPart[] }
|
|
30
|
+
export type StatuslineViewModel = {
|
|
31
|
+
layout: "wide" | "medium" | "narrow"
|
|
32
|
+
width: number
|
|
33
|
+
rows: StatuslineViewRow[]
|
|
34
|
+
}
|
|
35
|
+
export type StatuslineEconomicsValue = {
|
|
36
|
+
text: string
|
|
37
|
+
tone: StatuslineTone
|
|
38
|
+
used?: number
|
|
39
|
+
limit?: number
|
|
40
|
+
percent?: number
|
|
41
|
+
headroom?: number
|
|
42
|
+
}
|
|
43
|
+
export type StatuslineEconomics = {
|
|
44
|
+
cost: StatuslineEconomicsValue
|
|
45
|
+
context: StatuslineEconomicsValue
|
|
46
|
+
model: StatuslineEconomicsValue
|
|
47
|
+
status: StatuslineEconomicsValue
|
|
48
|
+
week: StatuslineEconomicsValue
|
|
49
|
+
modelRef?: string
|
|
50
|
+
}
|
|
51
|
+
export type StatuslineViewOptions = {
|
|
52
|
+
width?: number
|
|
53
|
+
economics?: StatuslineEconomics
|
|
54
|
+
promptRight?: boolean
|
|
55
|
+
}
|
|
56
|
+
export type StatuslineState = {
|
|
57
|
+
payload: StatuslinePayload
|
|
58
|
+
lastGood: StatuslinePayload | undefined
|
|
59
|
+
stale: boolean
|
|
60
|
+
unavailable: boolean
|
|
61
|
+
}
|
|
62
|
+
export type StatuslineContext = { cwd: string; session: string; width: number; model?: string }
|
|
63
|
+
export type StatuslineRefreshEvent =
|
|
64
|
+
| "server.connected"
|
|
65
|
+
| "project.updated"
|
|
66
|
+
| "session.created"
|
|
67
|
+
| "session.updated"
|
|
68
|
+
| "session.compacted"
|
|
69
|
+
| "session.deleted"
|
|
70
|
+
| "session.diff"
|
|
71
|
+
| "session.error"
|
|
72
|
+
| "session.idle"
|
|
73
|
+
| "session.status"
|
|
74
|
+
export type StatuslineRefreshTarget = { app: boolean; sessionID?: string }
|
|
75
|
+
|
|
76
|
+
export const STATUSLINE_VERSION: 1
|
|
77
|
+
export const STATUSLINE_SEPARATOR: string
|
|
78
|
+
export const STATUSLINE_FALLBACK: string
|
|
79
|
+
export const STATUSLINE_GROUP_SEPARATOR: string
|
|
80
|
+
export const STATUSLINE_WIDE_WIDTH: number
|
|
81
|
+
export const STATUSLINE_MEDIUM_WIDTH: number
|
|
82
|
+
export const DEFAULT_DEBOUNCE_MS: number
|
|
83
|
+
export const DEFAULT_INTERVAL_MS: number
|
|
84
|
+
export const DEFAULT_TIMEOUT_MS: number
|
|
85
|
+
export const STATUSLINE_REFRESH_EVENTS: readonly StatuslineRefreshEvent[]
|
|
86
|
+
|
|
87
|
+
export function parseStatuslineJson(raw: string): StatuslinePayload | undefined
|
|
88
|
+
export function statuslineRefreshTarget(event: unknown, activeSessionID?: string): StatuslineRefreshTarget
|
|
89
|
+
export function statuslineCommandArgs(context: StatuslineContext): string[]
|
|
90
|
+
export function statuslineBinaryCandidates(bin?: unknown, env?: Record<string, string | undefined>): string[]
|
|
91
|
+
export function runStatusline(input: StatuslineContext & {
|
|
92
|
+
bin?: unknown
|
|
93
|
+
env?: Record<string, string | undefined>
|
|
94
|
+
spawnImpl?: (...args: any[]) => any
|
|
95
|
+
timeoutMs?: number
|
|
96
|
+
signal?: AbortSignal
|
|
97
|
+
nowFn?: () => number
|
|
98
|
+
setTimeoutFn?: typeof setTimeout
|
|
99
|
+
clearTimeoutFn?: typeof clearTimeout
|
|
100
|
+
}): Promise<StatuslinePayload | undefined>
|
|
101
|
+
export function createSpawnLimiter(input?: { concurrency?: number }): {
|
|
102
|
+
run<Value>(
|
|
103
|
+
task: (signal: AbortSignal) => Value | Promise<Value>,
|
|
104
|
+
options?: { signal?: AbortSignal; priority?: number },
|
|
105
|
+
): Promise<Value | undefined>
|
|
106
|
+
activeCount(): number
|
|
107
|
+
queuedCount(): number
|
|
108
|
+
dispose(): void
|
|
109
|
+
}
|
|
110
|
+
export function cellWidth(text: string): number
|
|
111
|
+
export function deriveSessionEconomics(input?: {
|
|
112
|
+
session?: {
|
|
113
|
+
cost?: number
|
|
114
|
+
model?: { id: string; providerID: string }
|
|
115
|
+
}
|
|
116
|
+
messages?: ReadonlyArray<{
|
|
117
|
+
role: string
|
|
118
|
+
providerID?: string
|
|
119
|
+
modelID?: string
|
|
120
|
+
time?: { completed?: number }
|
|
121
|
+
error?: unknown
|
|
122
|
+
tokens?: {
|
|
123
|
+
input?: number
|
|
124
|
+
output?: number
|
|
125
|
+
reasoning?: number
|
|
126
|
+
cache?: { read?: number; write?: number }
|
|
127
|
+
}
|
|
128
|
+
}>
|
|
129
|
+
providers?: ReadonlyArray<{
|
|
130
|
+
id: string
|
|
131
|
+
models: Readonly<Record<string, {
|
|
132
|
+
id: string
|
|
133
|
+
providerID?: string
|
|
134
|
+
name?: string
|
|
135
|
+
limit?: { context?: number }
|
|
136
|
+
}>>
|
|
137
|
+
}>
|
|
138
|
+
status?: { type?: string }
|
|
139
|
+
}): StatuslineEconomics
|
|
140
|
+
export function selectSegments(
|
|
141
|
+
payload: StatuslinePayload,
|
|
142
|
+
options?: { width?: number; compact?: boolean },
|
|
143
|
+
): StatuslineSegment[]
|
|
144
|
+
export function initialStatuslineState(): StatuslineState
|
|
145
|
+
export function nextStatuslineState(previous: StatuslineState, payload?: StatuslinePayload): StatuslineState
|
|
146
|
+
export function renderSegments(
|
|
147
|
+
state: StatuslineState,
|
|
148
|
+
options?: { width?: number; compact?: boolean },
|
|
149
|
+
): StatuslineSegment[]
|
|
150
|
+
export function renderStatuslineText(
|
|
151
|
+
state: StatuslineState,
|
|
152
|
+
options?: StatuslineViewOptions & { compact?: boolean },
|
|
153
|
+
): string
|
|
154
|
+
export function createStatuslineViewModel(
|
|
155
|
+
state: StatuslineState,
|
|
156
|
+
options?: StatuslineViewOptions,
|
|
157
|
+
): StatuslineViewModel
|
|
158
|
+
export function renderStatuslineViewText(
|
|
159
|
+
state: StatuslineState,
|
|
160
|
+
options?: StatuslineViewOptions,
|
|
161
|
+
): string
|
|
162
|
+
|
|
163
|
+
export function createRefreshScheduler(input: {
|
|
164
|
+
run: () => void | Promise<void>
|
|
165
|
+
debounceMs?: number
|
|
166
|
+
intervalMs?: number
|
|
167
|
+
setTimeoutFn?: typeof setTimeout
|
|
168
|
+
clearTimeoutFn?: typeof clearTimeout
|
|
169
|
+
setIntervalFn?: typeof setInterval
|
|
170
|
+
clearIntervalFn?: typeof clearInterval
|
|
171
|
+
}): {
|
|
172
|
+
start(): void
|
|
173
|
+
request(immediate?: boolean): void
|
|
174
|
+
flush(): Promise<void>
|
|
175
|
+
dispose(): void
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function createStatuslineController(input: {
|
|
179
|
+
load: (
|
|
180
|
+
context: StatuslineContext,
|
|
181
|
+
signal: AbortSignal,
|
|
182
|
+
) => StatuslinePayload | undefined | Promise<StatuslinePayload | undefined>
|
|
183
|
+
onState?: (state: StatuslineState) => void
|
|
184
|
+
debounceMs?: number
|
|
185
|
+
intervalMs?: number
|
|
186
|
+
timers?: Record<string, unknown>
|
|
187
|
+
}): {
|
|
188
|
+
start(): void
|
|
189
|
+
request(immediate?: boolean): void
|
|
190
|
+
refreshNow(): Promise<void>
|
|
191
|
+
dispose(): void
|
|
192
|
+
setContext(context: Partial<StatuslineContext>): void
|
|
193
|
+
getState(): StatuslineState
|
|
194
|
+
getContext(): StatuslineContext
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function createKeyedControllerPool<Key, Value extends { dispose(): void }>(input: {
|
|
198
|
+
create: (key: Key) => Value
|
|
199
|
+
}): {
|
|
200
|
+
acquire(key: Key): { value: Value; release(): void }
|
|
201
|
+
get(key: Key): Value | undefined
|
|
202
|
+
forEach(callback: (value: Value, key: Key) => void): void
|
|
203
|
+
size(): number
|
|
204
|
+
dispose(): void
|
|
205
|
+
}
|