opencode-kiro 0.5.0-beta.1 → 0.5.0-beta.2
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
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# opencode-kiro
|
|
2
2
|
|
|
3
|
-
> ⚠️ **Experimental prerelease** — `0.5.0-beta.
|
|
3
|
+
> ⚠️ **Experimental prerelease** — `0.5.0-beta.2` targets the **unreleased OpenCode v2**
|
|
4
4
|
> plugin contract at a pinned snapshot. It does **not** work with OpenCode v1.
|
|
5
5
|
> v1 users: stay on **`opencode-kiro@0.4.0`** (the `main` branch / npm `latest` line,
|
|
6
6
|
> which remains the supported stable release). See
|
|
7
|
-
> [RELEASE_NOTES_0.5.0-beta.
|
|
7
|
+
> [RELEASE_NOTES_0.5.0-beta.2.md](./RELEASE_NOTES_0.5.0-beta.2.md) and
|
|
8
8
|
> [PINNED_VERSIONS.md](./PINNED_VERSIONS.md) for exact pins and the tested OpenCode SHA.
|
|
9
9
|
> No OpenCode v2 release date is known or claimed here.
|
|
10
10
|
|
|
@@ -20,7 +20,10 @@ The plugin supplies:
|
|
|
20
20
|
- **Provider ownership**: an AISDK hook constructs the provider from
|
|
21
21
|
[`kiro-acp-ai-provider`](https://www.npmjs.com/package/kiro-acp-ai-provider) with the
|
|
22
22
|
right options (`cwd`, `agent`, `trustAllTools`, `mcpTimeout`, `contextWindows`)
|
|
23
|
-
- **TUI credits display**: a live Kiro credits box in the sidebar
|
|
23
|
+
- **TUI credits display**: a live Kiro credits box in the sidebar and a compact
|
|
24
|
+
credits chip in the prompt footer row beside the host cost/context display, both
|
|
25
|
+
styled with the host's active theme tokens — auto-loaded from the same single
|
|
26
|
+
config entry (`tui: true`)
|
|
24
27
|
|
|
25
28
|
`kiro-acp-ai-provider` talks to your locally installed `kiro-cli` over Kiro's
|
|
26
29
|
[Agent Client Protocol](https://agentclientprotocol.com) (ACP). This is the supported
|
|
@@ -33,9 +36,9 @@ This prerelease is built and tested against **one pinned OpenCode v2 snapshot**:
|
|
|
33
36
|
|
|
34
37
|
| Item | Value |
|
|
35
38
|
|---|---|
|
|
36
|
-
| Tested OpenCode commit (`upstream/v2`) | `
|
|
37
|
-
| `@opencode-ai/plugin` | `0.0.0-
|
|
38
|
-
| Package version | `0.5.0-beta.
|
|
39
|
+
| Tested OpenCode commit (`upstream/v2`) | `1cf61593b5ec204619b3f679fe418fec10ca5934` |
|
|
40
|
+
| `@opencode-ai/plugin` | `0.0.0-dev-17968` (exact) |
|
|
41
|
+
| Package version | `0.5.0-beta.2` |
|
|
39
42
|
|
|
40
43
|
Full pin table and verification evidence: [PINNED_VERSIONS.md](./PINNED_VERSIONS.md).
|
|
41
44
|
There is no `engines.opencode` constraint — the v2 host has no stable semver yet; the
|
|
@@ -51,79 +54,81 @@ tested SHA above is the compatibility target. Other v2 snapshots may or may not
|
|
|
51
54
|
|
|
52
55
|
## Install and configure
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
**One config entry — that's the whole setup.** Add the package to the **`plugins`**
|
|
58
|
+
array (plural) of your OpenCode config (`opencode.json`, project or global):
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"plugins": ["opencode-kiro@0.5.0-beta.2"]
|
|
63
|
+
}
|
|
64
|
+
```
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
(`opencode.json`, project or global). The object form pins the version and leaves room
|
|
61
|
-
for future options:
|
|
66
|
+
The object form pins the same version and leaves room for future options:
|
|
62
67
|
|
|
63
68
|
```json
|
|
64
69
|
{
|
|
65
70
|
"plugins": [
|
|
66
71
|
{
|
|
67
|
-
"package": "opencode-kiro@0.5.0-beta.
|
|
72
|
+
"package": "opencode-kiro@0.5.0-beta.2",
|
|
68
73
|
"options": {}
|
|
69
74
|
}
|
|
70
75
|
]
|
|
71
76
|
}
|
|
72
77
|
```
|
|
73
78
|
|
|
74
|
-
A plain package string is also accepted:
|
|
75
|
-
|
|
76
|
-
```json
|
|
77
|
-
{
|
|
78
|
-
"plugins": ["opencode-kiro@0.5.0-beta.1"]
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
79
|
This loads the server entry (`./server` export): auth, model discovery, and provider
|
|
83
|
-
ownership.
|
|
80
|
+
ownership. The server plugin declares **`tui: true`**, so the host TUI **auto-loads the
|
|
81
|
+
package's `./tui` entrypoint by itself** — the sidebar credits box and the composer
|
|
82
|
+
credits chip appear with no further configuration. **No `cli.json` TUI entry is
|
|
83
|
+
needed.** (The beta.1 two-file setup is obsolete: if you added `"opencode-kiro"` to
|
|
84
|
+
your global `cli.json` `plugins` array for beta.1, remove it.)
|
|
84
85
|
|
|
85
|
-
###
|
|
86
|
+
### Disabling
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
One `"-kiro"` directive in the same `plugins` array disables **everything**: it removes
|
|
89
|
+
the server plugin, and with it the `tui: true` auto-load, so the TUI half never
|
|
90
|
+
activates either:
|
|
90
91
|
|
|
91
92
|
```json
|
|
92
93
|
{
|
|
93
|
-
"plugins": ["opencode-kiro"]
|
|
94
|
+
"plugins": ["opencode-kiro@0.5.0-beta.2", "-kiro"]
|
|
94
95
|
}
|
|
95
96
|
```
|
|
96
97
|
|
|
97
|
-
This loads the TUI entry (`./tui` export): the sidebar credits box. The connect flow
|
|
98
|
-
shows these same steps when you answer **Yes** to the
|
|
99
|
-
"Enable the Kiro credits sidebar?" prompt during `opencode auth login`.
|
|
100
|
-
|
|
101
98
|
### Legacy `tui.json` (v1)
|
|
102
99
|
|
|
103
100
|
`tui.json` is **legacy v1 configuration** and, under v2, is **migration input only**:
|
|
104
|
-
the host may read it when migrating old setups, and this plugin **never modifies it
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
the host may read it when migrating old setups, and this plugin **never modifies it**.
|
|
102
|
+
Do not add new entries to `tui.json` (or to `cli.json` — neither is used by this
|
|
103
|
+
plugin anymore); the single `plugins` entry above is the only configuration.
|
|
107
104
|
|
|
108
105
|
### Local development (path source)
|
|
109
106
|
|
|
110
|
-
Run a local checkout without npm — build first, then reference the
|
|
111
|
-
|
|
107
|
+
Run a local checkout without npm — build and pack first, then reference the tarball
|
|
108
|
+
with the `name@file:` form in the `plugins` array:
|
|
112
109
|
|
|
113
110
|
```bash
|
|
114
111
|
git clone https://github.com/NachoFLizaur/opencode-kiro && cd opencode-kiro
|
|
115
|
-
npm install && npm run build
|
|
112
|
+
npm install && npm run build && npm pack
|
|
116
113
|
```
|
|
117
114
|
|
|
118
115
|
```json
|
|
119
|
-
{ "plugins": ["
|
|
116
|
+
{ "plugins": ["opencode-kiro@file:/absolute/path/to/opencode-kiro-0.5.0-beta.2.tgz"] }
|
|
120
117
|
```
|
|
121
118
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
119
|
+
A bare path or bare `file:` spec is rejected at the tested SHA — the `name@file:` form
|
|
120
|
+
is required. **Caveat (local `file:` installs only)**: the colon in the resulting
|
|
121
|
+
install dirname defeats the host's OpenTUI loader shim, so the TUI surfaces render a
|
|
122
|
+
contained per-slot error notice instead of the credits views (the rest of the TUI keeps
|
|
123
|
+
working). Registry installs (`opencode-kiro@0.5.0-beta.2`) use colon-free paths and are
|
|
124
|
+
fully green — this affects local tarball validation only.
|
|
125
|
+
|
|
126
|
+
The host resolves entrypoints from the package `exports` (`./server` for the server
|
|
127
|
+
half, `./tui` for the auto-loaded TUI half). Each entry module exports its own `id`:
|
|
128
|
+
the server plugin's id is `kiro` and the TUI plugin's id is `opencode-kiro`. Under
|
|
129
|
+
`tui: true` there is no separate TUI directive to manage — `-kiro` (the server id) is
|
|
130
|
+
the single kill-switch, and host logs use `kiro` for the server half and
|
|
131
|
+
`opencode-kiro` for the TUI half.
|
|
127
132
|
|
|
128
133
|
## Auth
|
|
129
134
|
|
|
@@ -138,6 +143,11 @@ Select the **Kiro** integration, then the **Kiro CLI Login** method:
|
|
|
138
143
|
Complete the login there; the plugin polls for up to 120 seconds and stores a minimal
|
|
139
144
|
credential record when kiro-cli reports success.
|
|
140
145
|
|
|
146
|
+
There is no configuration prompt during login anymore: the beta.1
|
|
147
|
+
"Enable the Kiro credits sidebar?" consent select was removed (upstream deleted the
|
|
148
|
+
prompts API in favor of forms — and the sidebar no longer needs consent-driven config,
|
|
149
|
+
since `tui: true` auto-loads it).
|
|
150
|
+
|
|
141
151
|
If the flow times out, authenticate directly with kiro-cli (`kiro-cli login`) and run
|
|
142
152
|
`opencode auth login` again; the fast path then completes immediately.
|
|
143
153
|
|
|
@@ -176,42 +186,50 @@ so even the lowest level still produces a reasoning trail.
|
|
|
176
186
|
|
|
177
187
|
Kiro is subscription-metered: requests consume **credits**, and the dollar cost
|
|
178
188
|
OpenCode normally displays for Kiro turns is always $0.00. To surface credits the TUI
|
|
179
|
-
plugin renders
|
|
189
|
+
plugin renders two surfaces:
|
|
180
190
|
|
|
181
|
-
- a Kiro credits box in the sidebar (`sidebar.content`
|
|
191
|
+
- a Kiro credits box in the sidebar (`sidebar.content` claim), showing the session's
|
|
182
192
|
live credits total and unit
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
- a compact credits chip in the prompt footer row beside the host cost/context
|
|
194
|
+
display (`prompt.footer.status` claim, v1 placement restored) with the same total
|
|
195
|
+
|
|
196
|
+
Both are additive `append` claims — they compose with the host's built-in content and
|
|
197
|
+
never replace it — and both pick up the active theme's text tokens (feature-detected;
|
|
198
|
+
with no theme they fall back to default terminal styling). They render only for
|
|
199
|
+
sessions that carry Kiro credit data; other sessions are unchanged. The credits value
|
|
200
|
+
and unit come from the provider state the host persists on each message part
|
|
201
|
+
(`part.state.credits` / `part.state.creditsUnit`); nothing is hardcoded client-side.
|
|
202
|
+
The only configuration needed is the single `plugins` entry from
|
|
203
|
+
[Install](#install-and-configure) — the TUI half auto-loads via `tui: true`.
|
|
204
|
+
|
|
205
|
+
Durable credits are read straight from host message state (the host persists provider
|
|
206
|
+
state on text end — fixed upstream since beta.1). While a turn is still streaming,
|
|
207
|
+
credits for just-ended text are picked up live through a transient overlay that works
|
|
208
|
+
around a host reducer bug at the pinned snapshot (the live event path still drops
|
|
209
|
+
provider state); once durable state arrives it is authoritative and nothing is
|
|
210
|
+
double-counted. See the release notes for details.
|
|
195
211
|
|
|
196
212
|
## Known limitations (prerelease)
|
|
197
213
|
|
|
198
|
-
- **
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
matching the active theme.
|
|
204
|
-
- **Reduced toast feedback.** Auth-flow feedback is delivered as connect-flow text
|
|
205
|
-
(method instructions and the sidebar setup steps) rather than toasts. A toast API
|
|
206
|
-
exists at the pinned snapshot, but this plugin's core deliberately does not depend on
|
|
207
|
-
it — the TUI context surface is churning and toast availability is not guaranteed
|
|
208
|
-
across snapshots.
|
|
209
|
-
- **Live text credits use a workaround.** See [Credits in the TUI](#credits-in-the-tui)
|
|
210
|
-
and the release notes.
|
|
214
|
+
- **Live text credits use a transient overlay.** The durable credits path is fixed
|
|
215
|
+
upstream, but the live `session.text.ended` reducer still drops provider state at
|
|
216
|
+
the tested SHA, so in-turn updates come from the plugin's transient overlay
|
|
217
|
+
(durable state always wins on reconcile). See
|
|
218
|
+
[Credits in the TUI](#credits-in-the-tui) and the release notes.
|
|
211
219
|
- **Credits render in the TUI only.** Every other cost surface (ACP clients, web,
|
|
212
220
|
desktop, share pages, CLI cost output) shows $0.00 for Kiro sessions because the
|
|
213
221
|
catalog declares Kiro's per-token `cost` as 0 (subscription-metered, no per-token
|
|
214
222
|
pricing). That is expected, not a defect.
|
|
223
|
+
- **Local `file:` installs show a per-slot TUI error.** The colon in a `name@file:`
|
|
224
|
+
install dirname defeats the host's OpenTUI loader shim; the failure is contained to
|
|
225
|
+
the plugin's slots (dismissible error notice, host TUI unaffected). Registry
|
|
226
|
+
installs are colon-free and fully working — this affects local tarball validation
|
|
227
|
+
only.
|
|
228
|
+
- **Reduced toast feedback.** Auth-flow feedback is delivered as connect-flow text
|
|
229
|
+
rather than toasts; this plugin's core deliberately does not depend on the churning
|
|
230
|
+
TUI toast API.
|
|
231
|
+
- **One tested snapshot.** All pins are exact and the compatibility target is a single
|
|
232
|
+
OpenCode v2 SHA (see [Compatibility](#compatibility)); other snapshots may not work.
|
|
215
233
|
|
|
216
234
|
## How it works
|
|
217
235
|
|
|
@@ -241,10 +259,10 @@ details.
|
|
|
241
259
|
|---|---|
|
|
242
260
|
| `kiro-cli is not installed` during auth | Install kiro-cli from <https://kiro.dev/docs/cli/> and ensure it is on `PATH` for the opencode process. |
|
|
243
261
|
| Auth times out after ~120s | Complete the browser login faster, or run `kiro-cli login` yourself, then re-run `opencode auth login` (fast path). |
|
|
244
|
-
| No credits line / credits stay 0 | Credits appear after the first **completed** kiro turn; cancelled turns and turns without usage state contribute nothing. Check `"
|
|
245
|
-
| Credits
|
|
262
|
+
| No credits line / credits stay 0 | Credits appear after the first **completed** kiro turn; cancelled turns and turns without usage state contribute nothing. Check the plugin is active (no stray `"-kiro"` directive — note that directive residue can persist on a reused data dir). |
|
|
263
|
+
| Credits surfaces never appear | The TUI half auto-loads from the server `plugins` entry via `tui: true` — no separate TUI config exists. If the box/chip are missing, the server plugin itself is not loading (check your `plugins` entry and restart opencode). For local `name@file:` tarball installs, a contained per-slot error notice instead of the credits views is the known colon-path caveat; use a registry install. |
|
|
246
264
|
| `kiro` provider not showing in `opencode models` | Run `opencode auth login` first: models are discovered after auth. If the loaded catalog lacks a `kiro` entry, the plugin self-registers a minimal fallback during discovery. |
|
|
247
|
-
| Path install rejected (`must export id`) |
|
|
265
|
+
| Path install rejected (`must export id`) | Use the `name@file:<absolute tarball path>` form after `npm run build && npm pack` in your checkout (both entry modules export ids). |
|
|
248
266
|
| Provider visible but runs fail | The provider can be selectable before any credential exists. Run `opencode auth login` first. |
|
|
249
267
|
| Worked yesterday, broken today | This prerelease targets one pinned OpenCode snapshot (see [Compatibility](#compatibility)). If your OpenCode build moved past the tested SHA, the v2 plugin surface may have changed underneath it. |
|
|
250
268
|
|
|
@@ -255,7 +273,7 @@ v1 (`opencode >= 1.16.0`). It uses the v1 contract throughout: singular `plugin`
|
|
|
255
273
|
arrays in `opencode.json` and `tui.json`, the `opencode plugin opencode-kiro`
|
|
256
274
|
installer, and `part.metadata.kiro` credits. Its full documentation is the README at
|
|
257
275
|
the [`v0.4.0` tag](https://github.com/NachoFLizaur/opencode-kiro/tree/v0.4.0)
|
|
258
|
-
(equivalently, `main`). Do not install `0.5.0-beta.
|
|
276
|
+
(equivalently, `main`). Do not install `0.5.0-beta.2` into an OpenCode v1 setup.
|
|
259
277
|
|
|
260
278
|
## Development
|
|
261
279
|
|
|
@@ -3,30 +3,32 @@ import {
|
|
|
3
3
|
} from "./chunk-M7OUVABX.js";
|
|
4
4
|
|
|
5
5
|
// src/tui/credits-box-view.ts
|
|
6
|
-
import { createElement, insert, insertNode } from "@opentui/solid";
|
|
6
|
+
import { createElement, insert, insertNode, setProp } from "@opentui/solid";
|
|
7
7
|
import { createMemo } from "solid-js";
|
|
8
|
-
function createCreditsBoxView(credits) {
|
|
8
|
+
function createCreditsBoxView(credits, tokens) {
|
|
9
9
|
const current = createMemo(credits);
|
|
10
10
|
const root = createElement("box");
|
|
11
11
|
insertNode(
|
|
12
12
|
root,
|
|
13
|
-
headerLine(() => current().present ? "Kiro" : "")
|
|
13
|
+
headerLine(() => current().present ? "Kiro" : "", tokens?.default)
|
|
14
14
|
);
|
|
15
15
|
insertNode(
|
|
16
16
|
root,
|
|
17
|
-
plainLine(() => current().present ? formatCredits(current().total, current().unit) : "")
|
|
17
|
+
plainLine(() => current().present ? formatCredits(current().total, current().unit) : "", tokens?.subdued)
|
|
18
18
|
);
|
|
19
19
|
return root;
|
|
20
20
|
}
|
|
21
|
-
function headerLine(content) {
|
|
21
|
+
function headerLine(content, fg) {
|
|
22
22
|
const line = createElement("text");
|
|
23
|
+
if (fg !== void 0) setProp(line, "fg", fg);
|
|
23
24
|
const bold = createElement("b");
|
|
24
25
|
insert(bold, content);
|
|
25
26
|
insertNode(line, bold);
|
|
26
27
|
return line;
|
|
27
28
|
}
|
|
28
|
-
function plainLine(content) {
|
|
29
|
+
function plainLine(content, fg) {
|
|
29
30
|
const line = createElement("text");
|
|
31
|
+
if (fg !== void 0) setProp(line, "fg", fg);
|
|
30
32
|
insert(line, content);
|
|
31
33
|
return line;
|
|
32
34
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatCredits
|
|
3
|
+
} from "./chunk-M7OUVABX.js";
|
|
4
|
+
|
|
5
|
+
// src/tui/credits-chip-view.ts
|
|
6
|
+
import { createElement, insert, setProp } from "@opentui/solid";
|
|
7
|
+
import { createMemo } from "solid-js";
|
|
8
|
+
function createCreditsChipView(credits, tokens) {
|
|
9
|
+
const current = createMemo(credits);
|
|
10
|
+
const chip = createElement("text");
|
|
11
|
+
setProp(chip, "height", 1);
|
|
12
|
+
setProp(chip, "wrapMode", "none");
|
|
13
|
+
setProp(chip, "flexShrink", 0);
|
|
14
|
+
if (tokens?.subdued !== void 0) setProp(chip, "fg", tokens.subdued);
|
|
15
|
+
insert(chip, () => current().present ? formatCredits(current().total, current().unit) : "");
|
|
16
|
+
return chip;
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
createCreditsChipView
|
|
20
|
+
};
|
package/dist/server.js
CHANGED
|
@@ -1,74 +1,3 @@
|
|
|
1
|
-
// src/server/aisdk.ts
|
|
2
|
-
var KIRO_SDK_PACKAGE = "kiro-acp-ai-provider";
|
|
3
|
-
function createAisdkResources() {
|
|
4
|
-
return {
|
|
5
|
-
ownedSdks: /* @__PURE__ */ new Set(),
|
|
6
|
-
cache: /* @__PURE__ */ new Map(),
|
|
7
|
-
disposeHook: void 0
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
function isKiroPackage(pkg) {
|
|
11
|
-
return pkg === KIRO_SDK_PACKAGE || pkg === `aisdk:${KIRO_SDK_PACKAGE}`;
|
|
12
|
-
}
|
|
13
|
-
function isJsonSafe(value) {
|
|
14
|
-
if (value === null) return true;
|
|
15
|
-
const kind = typeof value;
|
|
16
|
-
if (kind === "string" || kind === "number" || kind === "boolean") return true;
|
|
17
|
-
if (Array.isArray(value)) return value.every(isJsonSafe);
|
|
18
|
-
if (kind === "object") {
|
|
19
|
-
const proto = Object.getPrototypeOf(value);
|
|
20
|
-
if (proto !== Object.prototype && proto !== null) return false;
|
|
21
|
-
return Object.values(value).every(isJsonSafe);
|
|
22
|
-
}
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
function stableStringify(value) {
|
|
26
|
-
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
27
|
-
if (typeof value === "object" && value !== null) {
|
|
28
|
-
const entries = Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`);
|
|
29
|
-
return `{${entries.join(",")}}`;
|
|
30
|
-
}
|
|
31
|
-
return JSON.stringify(value);
|
|
32
|
-
}
|
|
33
|
-
function stableOptionsKey(options) {
|
|
34
|
-
return isJsonSafe(options) ? stableStringify(options) : void 0;
|
|
35
|
-
}
|
|
36
|
-
async function registerAisdkHook(context, resources) {
|
|
37
|
-
const registration = await context.aisdk.hook("sdk", async (event) => {
|
|
38
|
-
if (!isKiroPackage(event.package)) return;
|
|
39
|
-
const { createKiroAcp } = await import("kiro-acp-ai-provider");
|
|
40
|
-
const key = stableOptionsKey(event.options);
|
|
41
|
-
let sdk = key === void 0 ? void 0 : resources.cache.get(key);
|
|
42
|
-
if (sdk === void 0) {
|
|
43
|
-
sdk = createKiroAcp(event.options);
|
|
44
|
-
resources.ownedSdks.add(sdk);
|
|
45
|
-
if (key !== void 0) resources.cache.set(key, sdk);
|
|
46
|
-
}
|
|
47
|
-
event.sdk = sdk;
|
|
48
|
-
});
|
|
49
|
-
resources.disposeHook = registration.dispose;
|
|
50
|
-
return async () => {
|
|
51
|
-
const errors = [];
|
|
52
|
-
resources.disposeHook = void 0;
|
|
53
|
-
try {
|
|
54
|
-
await registration.dispose();
|
|
55
|
-
} catch (error) {
|
|
56
|
-
errors.push(error);
|
|
57
|
-
}
|
|
58
|
-
const owned = Array.from(resources.ownedSdks);
|
|
59
|
-
resources.ownedSdks.clear();
|
|
60
|
-
resources.cache.clear();
|
|
61
|
-
for (const sdk of owned) {
|
|
62
|
-
try {
|
|
63
|
-
await sdk.shutdown();
|
|
64
|
-
} catch (error) {
|
|
65
|
-
errors.push(error);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (errors.length > 0) throw new AggregateError(errors, "kiro aisdk cleanup failures");
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
1
|
// src/server/auth.ts
|
|
73
2
|
var KIRO_INTEGRATION_ID = "kiro";
|
|
74
3
|
var KIRO_INTEGRATION_NAME = "Kiro";
|
|
@@ -81,7 +10,6 @@ var NOT_INSTALLED_MESSAGE = "kiro-cli is not installed. Install it from https://
|
|
|
81
10
|
var TIMEOUT_MESSAGE = "Kiro authentication timed out. Run `kiro-cli login` manually, then re-run `opencode auth login`.";
|
|
82
11
|
var LOGIN_INSTRUCTIONS = "Complete Kiro authentication in the browser window that just opened. Waiting for login...";
|
|
83
12
|
var ALREADY_AUTHENTICATED_INSTRUCTIONS = "Already authenticated with Kiro CLI.";
|
|
84
|
-
var SIDEBAR_INSTRUCTIONS = 'To enable the Kiro credits sidebar, add "opencode-kiro" to the "plugins" array in your global cli.json and restart opencode.';
|
|
85
13
|
function createAuthResources() {
|
|
86
14
|
return {
|
|
87
15
|
child: void 0,
|
|
@@ -114,11 +42,6 @@ function kiroCredential() {
|
|
|
114
42
|
expires: 0
|
|
115
43
|
};
|
|
116
44
|
}
|
|
117
|
-
function withSidebarGuidance(instructions, inputs) {
|
|
118
|
-
return inputs["sidebar"] === "yes" ? `${instructions}
|
|
119
|
-
|
|
120
|
-
${SIDEBAR_INSTRUCTIONS}` : instructions;
|
|
121
|
-
}
|
|
122
45
|
function pollForLogin(verifyAuth, resources) {
|
|
123
46
|
return new Promise((resolve, reject) => {
|
|
124
47
|
const start = Date.now();
|
|
@@ -144,14 +67,14 @@ function pollForLogin(verifyAuth, resources) {
|
|
|
144
67
|
resources.pollTimer = setTimeout(tick, POLL_INTERVAL_MS);
|
|
145
68
|
});
|
|
146
69
|
}
|
|
147
|
-
async function authorize(
|
|
70
|
+
async function authorize(resources) {
|
|
148
71
|
const { verifyAuth } = await import("kiro-acp-ai-provider");
|
|
149
72
|
const status = verifyAuth();
|
|
150
73
|
if (!status.installed) throw new Error(NOT_INSTALLED_MESSAGE);
|
|
151
74
|
if (status.authenticated) {
|
|
152
75
|
return {
|
|
153
76
|
url: KIRO_DOCS_URL,
|
|
154
|
-
instructions:
|
|
77
|
+
instructions: ALREADY_AUTHENTICATED_INSTRUCTIONS,
|
|
155
78
|
mode: "auto",
|
|
156
79
|
callback: Promise.resolve(kiroCredential())
|
|
157
80
|
};
|
|
@@ -162,7 +85,7 @@ async function authorize(inputs, resources) {
|
|
|
162
85
|
});
|
|
163
86
|
return {
|
|
164
87
|
url: KIRO_DOCS_URL,
|
|
165
|
-
instructions:
|
|
88
|
+
instructions: LOGIN_INSTRUCTIONS,
|
|
166
89
|
mode: "auto",
|
|
167
90
|
callback: pollForLogin(verifyAuth, resources)
|
|
168
91
|
};
|
|
@@ -177,20 +100,9 @@ async function registerAuth(context, resources) {
|
|
|
177
100
|
method: {
|
|
178
101
|
id: KIRO_OAUTH_METHOD_ID,
|
|
179
102
|
type: "oauth",
|
|
180
|
-
label: KIRO_OAUTH_METHOD_LABEL
|
|
181
|
-
prompts: [
|
|
182
|
-
{
|
|
183
|
-
type: "select",
|
|
184
|
-
key: "sidebar",
|
|
185
|
-
message: "Enable the Kiro credits sidebar?",
|
|
186
|
-
options: [
|
|
187
|
-
{ label: "Yes", value: "yes", hint: "shows manual cli.json setup steps" },
|
|
188
|
-
{ label: "No", value: "no" }
|
|
189
|
-
]
|
|
190
|
-
}
|
|
191
|
-
]
|
|
103
|
+
label: KIRO_OAUTH_METHOD_LABEL
|
|
192
104
|
},
|
|
193
|
-
authorize: (
|
|
105
|
+
authorize: async (_answer) => authorize(resources)
|
|
194
106
|
// no refresh callback: kiro-cli owns credential storage and refresh
|
|
195
107
|
});
|
|
196
108
|
});
|
|
@@ -374,6 +286,81 @@ async function registerDiscovery(context, resources) {
|
|
|
374
286
|
};
|
|
375
287
|
}
|
|
376
288
|
|
|
289
|
+
// src/server/aisdk.ts
|
|
290
|
+
var KIRO_SDK_PACKAGE = "kiro-acp-ai-provider";
|
|
291
|
+
function createAisdkResources() {
|
|
292
|
+
return {
|
|
293
|
+
ownedSdks: /* @__PURE__ */ new Set(),
|
|
294
|
+
cache: /* @__PURE__ */ new Map(),
|
|
295
|
+
disposeHook: void 0
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function isKiroPackage(pkg) {
|
|
299
|
+
return pkg === KIRO_SDK_PACKAGE || pkg === `aisdk:${KIRO_SDK_PACKAGE}`;
|
|
300
|
+
}
|
|
301
|
+
function isJsonSafe(value) {
|
|
302
|
+
if (value === null) return true;
|
|
303
|
+
const kind = typeof value;
|
|
304
|
+
if (kind === "string" || kind === "number" || kind === "boolean") return true;
|
|
305
|
+
if (Array.isArray(value)) return value.every(isJsonSafe);
|
|
306
|
+
if (kind === "object") {
|
|
307
|
+
const proto = Object.getPrototypeOf(value);
|
|
308
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
309
|
+
return Object.values(value).every(isJsonSafe);
|
|
310
|
+
}
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
function stableStringify(value) {
|
|
314
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
315
|
+
if (typeof value === "object" && value !== null) {
|
|
316
|
+
const entries = Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`);
|
|
317
|
+
return `{${entries.join(",")}}`;
|
|
318
|
+
}
|
|
319
|
+
return JSON.stringify(value);
|
|
320
|
+
}
|
|
321
|
+
function stableOptionsKey(options) {
|
|
322
|
+
return isJsonSafe(options) ? stableStringify(options) : void 0;
|
|
323
|
+
}
|
|
324
|
+
async function registerAisdkHook(context, resources) {
|
|
325
|
+
const registration = await context.aisdk.hook(
|
|
326
|
+
"sdk",
|
|
327
|
+
async (event) => {
|
|
328
|
+
if (!isKiroPackage(event.package)) return;
|
|
329
|
+
const { createKiroAcp } = await import("kiro-acp-ai-provider");
|
|
330
|
+
const key = stableOptionsKey(event.options);
|
|
331
|
+
let sdk = key === void 0 ? void 0 : resources.cache.get(key);
|
|
332
|
+
if (sdk === void 0) {
|
|
333
|
+
sdk = createKiroAcp(event.options);
|
|
334
|
+
resources.ownedSdks.add(sdk);
|
|
335
|
+
if (key !== void 0) resources.cache.set(key, sdk);
|
|
336
|
+
}
|
|
337
|
+
event.sdk = sdk;
|
|
338
|
+
},
|
|
339
|
+
{ providerID: KIRO_PROVIDER_ID }
|
|
340
|
+
);
|
|
341
|
+
resources.disposeHook = registration.dispose;
|
|
342
|
+
return async () => {
|
|
343
|
+
const errors = [];
|
|
344
|
+
resources.disposeHook = void 0;
|
|
345
|
+
try {
|
|
346
|
+
await registration.dispose();
|
|
347
|
+
} catch (error) {
|
|
348
|
+
errors.push(error);
|
|
349
|
+
}
|
|
350
|
+
const owned = Array.from(resources.ownedSdks);
|
|
351
|
+
resources.ownedSdks.clear();
|
|
352
|
+
resources.cache.clear();
|
|
353
|
+
for (const sdk of owned) {
|
|
354
|
+
try {
|
|
355
|
+
await sdk.shutdown();
|
|
356
|
+
} catch (error) {
|
|
357
|
+
errors.push(error);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (errors.length > 0) throw new AggregateError(errors, "kiro aisdk cleanup failures");
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
377
364
|
// src/server/lifecycle.ts
|
|
378
365
|
function createServerState() {
|
|
379
366
|
return {
|
|
@@ -409,6 +396,9 @@ function buildCleanup(state) {
|
|
|
409
396
|
// src/server.ts
|
|
410
397
|
var plugin = {
|
|
411
398
|
id: "kiro",
|
|
399
|
+
// tui: true (dist/promise/plugin.d.ts:40) — the host auto-loads this
|
|
400
|
+
// package's `./tui` entrypoint for npm-channel installs (single config entry)
|
|
401
|
+
tui: true,
|
|
412
402
|
async setup(context) {
|
|
413
403
|
const state = createServerState();
|
|
414
404
|
const cleanup = buildCleanup(state);
|
package/dist/tui.js
CHANGED
|
@@ -81,6 +81,21 @@ function readSessionID(props) {
|
|
|
81
81
|
const sessionID = props.sessionID;
|
|
82
82
|
return typeof sessionID === "string" && sessionID.length > 0 ? sessionID : void 0;
|
|
83
83
|
}
|
|
84
|
+
function readColorToken(value) {
|
|
85
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
86
|
+
if (typeof value === "object" && value !== null) return value;
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
function readThemeTokens(theme) {
|
|
90
|
+
if (typeof theme !== "object" || theme === null) return void 0;
|
|
91
|
+
const text = theme.text;
|
|
92
|
+
if (typeof text !== "object" || text === null) return void 0;
|
|
93
|
+
const tokens = text;
|
|
94
|
+
const defaultFg = readColorToken(tokens.default);
|
|
95
|
+
const subduedFg = readColorToken(tokens.subdued);
|
|
96
|
+
if (defaultFg === void 0 && subduedFg === void 0) return void 0;
|
|
97
|
+
return { default: defaultFg, subdued: subduedFg };
|
|
98
|
+
}
|
|
84
99
|
var plugin = {
|
|
85
100
|
// `id` is required for file-source installs (opencode rejects them without one);
|
|
86
101
|
// matching the package name keeps it identical across path and npm installs
|
|
@@ -88,10 +103,14 @@ var plugin = {
|
|
|
88
103
|
async setup(context) {
|
|
89
104
|
const disposers = [];
|
|
90
105
|
let disposed = false;
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
106
|
+
const memoryStore = context.storage?.memory?.("transient-credits", {
|
|
107
|
+
initial: createTransientStore()
|
|
108
|
+
});
|
|
109
|
+
const store = memoryStore ? memoryStore[0] : createTransientStore();
|
|
110
|
+
if (!memoryStore) disposers.push(() => clear(store));
|
|
111
|
+
const [{ createCreditsBoxView }, { createCreditsChipView }, { createSignal }] = await Promise.all([
|
|
112
|
+
import("./credits-box-view-GLFJSV6M.js"),
|
|
113
|
+
import("./credits-chip-view-YP4RPX26.js"),
|
|
95
114
|
import("solid-js")
|
|
96
115
|
]);
|
|
97
116
|
const [transientVersion, setTransientVersion] = createSignal(0);
|
|
@@ -110,12 +129,24 @@ var plugin = {
|
|
|
110
129
|
reconcile(store, sessionID, messages);
|
|
111
130
|
return mergedMessageCredits(store, sessionID, messages);
|
|
112
131
|
};
|
|
113
|
-
const unregisterSidebar = context.ui.slot(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
132
|
+
const unregisterSidebar = context.ui.slot({
|
|
133
|
+
append: "sidebar.content",
|
|
134
|
+
render: (props) => {
|
|
135
|
+
const credits = creditsFor(readSessionID(props));
|
|
136
|
+
const view = createCreditsBoxView(credits, readThemeTokens(context.theme));
|
|
137
|
+
return (() => credits().present ? view : null);
|
|
138
|
+
}
|
|
117
139
|
});
|
|
118
140
|
disposers.push(unregisterSidebar);
|
|
141
|
+
const unregisterChip = context.ui.slot({
|
|
142
|
+
append: "prompt.footer.status",
|
|
143
|
+
render: (props) => {
|
|
144
|
+
const credits = creditsFor(readSessionID(props));
|
|
145
|
+
const view = createCreditsChipView(credits, readThemeTokens(context.theme));
|
|
146
|
+
return (() => credits().present ? view : null);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
disposers.push(unregisterChip);
|
|
119
150
|
return async () => {
|
|
120
151
|
if (disposed) return;
|
|
121
152
|
disposed = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-kiro",
|
|
3
|
-
"version": "0.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.2",
|
|
4
4
|
"description": "The ACP-compliant Kiro plugin for opencode: auth via the official kiro-cli login, the live Kiro model lineup through the Agent Client Protocol, and TUI credits display",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Nacho F. Lizaur (https://github.com/NachoFLizaur)",
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"node": ">=20"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@opencode-ai/plugin": "0.0.0-
|
|
42
|
+
"@opencode-ai/plugin": "0.0.0-dev-17968"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@opentui/solid": "0.
|
|
45
|
+
"@opentui/solid": "0.5.7",
|
|
46
46
|
"kiro-acp-ai-provider": "3.0.0",
|
|
47
47
|
"solid-js": "1.9.12"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@opencode-ai/plugin": "0.0.0-
|
|
50
|
+
"@opencode-ai/plugin": "0.0.0-dev-17968",
|
|
51
51
|
"@types/node": "^20.19.42",
|
|
52
52
|
"tsup": "^8.0.0",
|
|
53
53
|
"typescript": "^5.7.0",
|