opencode-kiro 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +239 -0
- package/dist/chunk-JO7U4OSF.js +56 -0
- package/dist/context-view-CGBZAYM5.js +62 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.js +91 -0
- package/dist/tui.d.ts +36 -0
- package/dist/tui.js +29 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nacho F. Lizaur
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# opencode-kiro
|
|
2
|
+
|
|
3
|
+
The ACP-compliant [Kiro](https://kiro.dev) auth plugin for [opencode](https://opencode.ai).
|
|
4
|
+
|
|
5
|
+
opencode learns the `kiro` provider and its 12 models from the
|
|
6
|
+
[models.dev](https://models.dev) catalog, exactly like the first-party Copilot and
|
|
7
|
+
GitLab integrations. This plugin supplies the piece the catalog can't: the **auth**.
|
|
8
|
+
|
|
9
|
+
- **Auth** via the official `kiro-cli` login flow (`opencode auth login`, then "Kiro CLI Login")
|
|
10
|
+
- **Provider options loader**: the `cwd`, `agent`, `trustAllTools`, `mcpTimeout` values
|
|
11
|
+
opencode forwards into the SDK factory
|
|
12
|
+
- **TUI credits display**: an opt-in sidebar context box showing tokens, context usage,
|
|
13
|
+
and Kiro credits
|
|
14
|
+
|
|
15
|
+
The `kiro` provider resolves to [`kiro-acp-ai-provider`](https://www.npmjs.com/package/kiro-acp-ai-provider),
|
|
16
|
+
an AI-SDK provider that talks to your locally installed `kiro-cli` over Kiro's
|
|
17
|
+
[Agent Client Protocol](https://agentclientprotocol.com) (ACP); opencode picks it up from
|
|
18
|
+
the catalog's `npm` field. This is the supported integration path: requests go through
|
|
19
|
+
kiro-cli exactly like Kiro's own IDE clients, with no credential scraping and no reuse of
|
|
20
|
+
Kiro credentials against other providers.
|
|
21
|
+
|
|
22
|
+
Not affiliated with any other kiro-named npm packages.
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
25
|
+
|
|
26
|
+
| Requirement | Notes |
|
|
27
|
+
|---|---|
|
|
28
|
+
| [kiro-cli](https://kiro.dev/docs/cli/) | Must be installed and on `PATH`; a Kiro subscription / AWS Builder ID account |
|
|
29
|
+
| opencode `>= 1.16.0` | Enforced via `engines.opencode` on released builds. The shipped catalog must include the `kiro` provider (see [Troubleshooting](#troubleshooting)). |
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
opencode plugin opencode-kiro
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
(alias: `opencode plug opencode-kiro`; add `--global`/`-g` to install into your global config instead of the project)
|
|
38
|
+
|
|
39
|
+
The installer reads this package's `exports` and detects both plugin entrypoints
|
|
40
|
+
(`./server` and `./tui`), then patches **both** config files automatically:
|
|
41
|
+
|
|
42
|
+
- `.opencode/opencode.json`: the server plugin (auth)
|
|
43
|
+
- `.opencode/tui.json`: the TUI plugin (credits sidebar box)
|
|
44
|
+
|
|
45
|
+
(with `--global`: `~/.config/opencode/opencode.json` and `~/.config/opencode/tui.json`)
|
|
46
|
+
|
|
47
|
+
You do **not** add a `provider.kiro` block; opencode loads the `kiro` provider and its
|
|
48
|
+
models straight from the models.dev catalog. Then disable the builtin context box, see
|
|
49
|
+
[Credits in the sidebar](#credits-in-the-sidebar).
|
|
50
|
+
|
|
51
|
+
### Manual alternative
|
|
52
|
+
|
|
53
|
+
Add the package name to the `plugin` array of **both** files yourself (in the
|
|
54
|
+
project's `.opencode/` directory, at the project root, or in the global
|
|
55
|
+
`~/.config/opencode/`, all are valid config locations):
|
|
56
|
+
|
|
57
|
+
`opencode.json`:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"plugin": ["opencode-kiro"]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`tui.json`:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"plugin": ["opencode-kiro"],
|
|
70
|
+
"plugin_enabled": { "internal:sidebar-context": false }
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
(`plugin_enabled` is the [credits sidebar](#credits-in-the-sidebar) step, recommended now, explained below.)
|
|
75
|
+
|
|
76
|
+
### Local development (path source)
|
|
77
|
+
|
|
78
|
+
Run a local checkout without npm: build first, then reference the repo directory by
|
|
79
|
+
absolute path in both `plugin` arrays:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
git clone https://github.com/NachoFLizaur/opencode-kiro && cd opencode-kiro
|
|
83
|
+
npm install && npm run build
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{ "plugin": ["/absolute/path/to/opencode-kiro"] }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
opencode resolves the right entrypoint per file from the package `exports`. Note that
|
|
91
|
+
path-sourced TUI plugins **must export an `id`** (opencode rejects them otherwise);
|
|
92
|
+
this package ships `{ id: "opencode-kiro", tui }`, so the id (and your
|
|
93
|
+
`plugin_enabled` keys) are identical across path and npm installs.
|
|
94
|
+
|
|
95
|
+
Because the provider now comes from the catalog rather than the plugin, a local checkout
|
|
96
|
+
also needs a catalog that includes `kiro`. opencode reads its catalog from
|
|
97
|
+
`OPENCODE_MODELS_PATH` when set; point it at an `api.json` that contains the `kiro`
|
|
98
|
+
provider (for example one generated from a [models.dev](https://models.dev) checkout):
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
OPENCODE_MODELS_PATH=/path/to/api.json opencode models | grep '^kiro/'
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Until the catalog opencode loads contains `kiro`, the provider will not appear regardless
|
|
105
|
+
of this plugin being installed (the plugin only adds auth, not the provider definition).
|
|
106
|
+
|
|
107
|
+
## Auth
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
opencode auth login
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Select the **Kiro (plugin)** provider, then the **Kiro CLI Login** method:
|
|
114
|
+
|
|
115
|
+
- **Already logged in to kiro-cli**: immediate success; the existing kiro-cli session is reused.
|
|
116
|
+
- **Not logged in**: the plugin launches `kiro-cli login`, which opens a browser window.
|
|
117
|
+
Complete the login there; the plugin polls for up to 120 seconds and stores the
|
|
118
|
+
credential when kiro-cli reports success.
|
|
119
|
+
|
|
120
|
+
If the flow times out, authenticate directly with kiro-cli (`kiro-cli login`) and run
|
|
121
|
+
`opencode auth login` again; the fast path then completes immediately.
|
|
122
|
+
|
|
123
|
+
> **Note:** opencode will also surface `kiro` if the `KIRO_API_KEY` environment variable
|
|
124
|
+
> is set, because the catalog entry declares `env: ["KIRO_API_KEY"]`. The intended auth
|
|
125
|
+
> path for this plugin is still **Kiro CLI Login** above (it reuses your local kiro-cli
|
|
126
|
+
> session); the env var is a secondary route opencode offers for any catalog provider.
|
|
127
|
+
|
|
128
|
+
## Models
|
|
129
|
+
|
|
130
|
+
12 models, defined in the [models.dev](https://models.dev) `kiro` catalog entry. opencode
|
|
131
|
+
loads them from there; the table below is a convenience snapshot, not the source of
|
|
132
|
+
truth. All models support tool calling and have a 64K-token output limit.
|
|
133
|
+
|
|
134
|
+
| Model | ID | Context window | Image input |
|
|
135
|
+
|---|---|---|---|
|
|
136
|
+
| Auto | `auto` | 1,000,000 | yes |
|
|
137
|
+
| Claude Haiku 4.5 | `claude-haiku-4.5` | 200,000 | yes |
|
|
138
|
+
| Claude Opus 4.5 | `claude-opus-4.5` | 200,000 | yes |
|
|
139
|
+
| Claude Opus 4.6 | `claude-opus-4.6` | 1,000,000 | yes |
|
|
140
|
+
| Claude Opus 4.7 | `claude-opus-4.7` | 1,000,000 | yes |
|
|
141
|
+
| Claude Sonnet 4 | `claude-sonnet-4` | 200,000 | yes |
|
|
142
|
+
| Claude Sonnet 4.5 | `claude-sonnet-4.5` | 200,000 | yes |
|
|
143
|
+
| Claude Sonnet 4.6 | `claude-sonnet-4.6` | 1,000,000 | yes |
|
|
144
|
+
| Deepseek v3.2 | `deepseek-3.2` | 164,000 | yes |
|
|
145
|
+
| MiniMax M2.1 | `minimax-m2.1` | 196,000 | yes |
|
|
146
|
+
| MiniMax M2.5 | `minimax-m2.5` | 196,000 | no |
|
|
147
|
+
| Qwen3 Coder Next | `qwen3-coder-next` | 256,000 | yes |
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
opencode models # lists kiro/auto, kiro/claude-sonnet-4.6, ...
|
|
151
|
+
opencode run -m kiro/auto "hello"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Image input is capability-driven: paste an image path into the TUI prompt and
|
|
155
|
+
image-capable models receive it as an attachment.
|
|
156
|
+
|
|
157
|
+
## Credits in the sidebar
|
|
158
|
+
|
|
159
|
+
Kiro is subscription-metered: requests consume **credits**, and the dollar cost
|
|
160
|
+
opencode normally displays is always $0.00. The plugin therefore registers a
|
|
161
|
+
**full replacement** for the built-in sidebar context box, showing tokens, context
|
|
162
|
+
percentage, and session credits.
|
|
163
|
+
|
|
164
|
+
Disable the builtin box in `tui.json` so only the replacement renders:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"plugin_enabled": { "internal:sidebar-context": false }
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Without this you will see **two** context boxes (cosmetic duplication: the builtin
|
|
173
|
+
one plus the plugin's). The credits value and its unit come from provider metadata
|
|
174
|
+
emitted by the SDK (kiro-cli reports the unit); nothing is hardcoded client-side.
|
|
175
|
+
|
|
176
|
+
Trade-off: the replacement box applies to **every** session and disabling the builtin
|
|
177
|
+
is global: mixed-provider users lose the builtin `$X.XX spent` line for non-Kiro
|
|
178
|
+
sessions too; if you need dollar cost there, leave the builtin enabled at the cost of
|
|
179
|
+
the duplicate box.
|
|
180
|
+
|
|
181
|
+
## Known limitation (read this)
|
|
182
|
+
|
|
183
|
+
**Credits display is TUI-sidebar-only.** Every other cost surface (the prompt footer,
|
|
184
|
+
ACP clients, the web app, desktop, web share pages, and CLI cost output) shows $0.00
|
|
185
|
+
for Kiro sessions. The models.dev catalog declares Kiro's per-token `cost` as 0 (it is a
|
|
186
|
+
subscription-metered provider with no per-token pricing), so opencode core computes $0.00
|
|
187
|
+
everywhere it renders dollar cost. That is expected, not a defect. A cross-surface credits
|
|
188
|
+
display would require opencode core changes and is intentionally out of scope for this
|
|
189
|
+
plugin.
|
|
190
|
+
|
|
191
|
+
## How it works
|
|
192
|
+
|
|
193
|
+
- **Provider & models (models.dev)**: opencode loads the `kiro` provider and its full
|
|
194
|
+
model list from the models.dev catalog. This plugin does **not** define any provider or
|
|
195
|
+
model config of its own; there is no `config` hook.
|
|
196
|
+
- **SDK resolution (resolveSDK)**: opencode reads the catalog's `npm` field
|
|
197
|
+
(`kiro-acp-ai-provider`), installs that package into its package cache on first model
|
|
198
|
+
use, and imports it. This plugin's `auth` loader supplies the provider options
|
|
199
|
+
(`cwd`, `agent`, `trustAllTools`, `mcpTimeout`, `contextWindows`) that opencode forwards
|
|
200
|
+
into `createKiroAcp(...)`. The loader relays each model's `limit.context` (from
|
|
201
|
+
models.dev, via opencode's resolved catalog) into the SDK's `contextWindows` map keyed
|
|
202
|
+
by `api.id`, so the SDK keeps no hardcoded per-model data and falls back to 1,000,000
|
|
203
|
+
for any model absent from the relay.
|
|
204
|
+
- **Auth (this plugin)**: registers the "Kiro CLI Login" OAuth method (kiro-cli login
|
|
205
|
+
flow) plus the options loader above. The same plugin also imports `verifyAuth` from
|
|
206
|
+
`kiro-acp-ai-provider` to check kiro-cli installation/login state.
|
|
207
|
+
- **Session affinity & reset (in-SDK)**: the SDK keys kiro-cli sessions off opencode's
|
|
208
|
+
`x-session-affinity` header, isolates tool-less utility calls (title generation) on
|
|
209
|
+
an ephemeral session, detects prompt-history divergence (fork/`/undo`), and starts a
|
|
210
|
+
fresh kiro session when needed. No host-side session plumbing.
|
|
211
|
+
- **Credits metadata**: the SDK attaches `{ kiro: { credits, creditsUnit } }` provider
|
|
212
|
+
metadata to the final part of each turn; opencode persists it, and the TUI plugin
|
|
213
|
+
sums it per assistant message (deduped across text/reasoning parts) for the sidebar.
|
|
214
|
+
|
|
215
|
+
## Troubleshooting
|
|
216
|
+
|
|
217
|
+
| Symptom | Cause / fix |
|
|
218
|
+
|---|---|
|
|
219
|
+
| `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. |
|
|
220
|
+
| Auth times out after ~120s | Complete the browser login faster, or run `kiro-cli login` yourself, then re-run `opencode auth login` (fast path). |
|
|
221
|
+
| Two "Context" boxes in the sidebar | Add `"plugin_enabled": { "internal:sidebar-context": false }` to `tui.json` (see [Credits in the sidebar](#credits-in-the-sidebar)). |
|
|
222
|
+
| No credits line / credits stay 0 | Credits appear after the first **completed** kiro turn; cancelled turns and turns without usage metadata contribute nothing. Check the TUI plugin is `active` in the Plugins dialog (and listed in `tui.json`). |
|
|
223
|
+
| `kiro` provider not showing in `opencode models` | The provider comes from the models.dev catalog, not this plugin. Ensure your opencode version ships a catalog that includes `kiro` (run `opencode models --refresh` to update the cache). For local development, point opencode at a kiro-inclusive catalog via `OPENCODE_MODELS_PATH=/path/to/api.json` (see [Local development](#local-development-path-source)). |
|
|
224
|
+
| `sdk.languageModel is not a function` | A stale `kiro-acp-ai-provider` < 2.0.0 resolved from opencode's package cache. Remove the cached copy (`$XDG_CACHE_HOME/opencode/packages/kiro-acp-ai-provider`, default `~/.cache/opencode/packages/...`) and retry; 2.0.0 fixed the factory auto-discovery clash. |
|
|
225
|
+
| Path install rejected (`must export id`) | Run `npm run build` in your checkout first and reference the repo root (both entry modules export ids). |
|
|
226
|
+
| Provider visible but runs fail | The provider is selectable (from the catalog) before any credential exists. Run `opencode auth login` first. |
|
|
227
|
+
|
|
228
|
+
## Development
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
npm install
|
|
232
|
+
npm run typecheck # tsc --noEmit
|
|
233
|
+
npm run build # tsup builds dist/server.js + dist/tui.js (+ d.ts)
|
|
234
|
+
npm test # vitest
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// src/tui/credits.ts
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null;
|
|
4
|
+
}
|
|
5
|
+
function readPartCredits(part) {
|
|
6
|
+
if (!isRecord(part)) return void 0;
|
|
7
|
+
const metadata = part.metadata;
|
|
8
|
+
if (!isRecord(metadata)) return void 0;
|
|
9
|
+
const kiro = metadata.kiro;
|
|
10
|
+
if (!isRecord(kiro)) return void 0;
|
|
11
|
+
if (typeof kiro.credits !== "number" || !Number.isFinite(kiro.credits)) return void 0;
|
|
12
|
+
return {
|
|
13
|
+
credits: kiro.credits,
|
|
14
|
+
unit: typeof kiro.creditsUnit === "string" && kiro.creditsUnit.length > 0 ? kiro.creditsUnit : void 0
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function messageCredits(parts) {
|
|
18
|
+
const carriers = parts.map(readPartCredits).filter((value) => value !== void 0);
|
|
19
|
+
const last = carriers.at(-1);
|
|
20
|
+
if (!last) return void 0;
|
|
21
|
+
return {
|
|
22
|
+
credits: last.credits,
|
|
23
|
+
unit: last.unit ?? carriers.findLast((carrier) => carrier.unit !== void 0)?.unit
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function creditsForMessage(parts) {
|
|
27
|
+
return messageCredits(parts)?.credits;
|
|
28
|
+
}
|
|
29
|
+
function sumSessionCredits(messages, partsByMessage) {
|
|
30
|
+
return messages.filter((message) => message.role === "assistant").reduce(
|
|
31
|
+
(acc, message) => {
|
|
32
|
+
const hit = messageCredits(partsByMessage(message.id));
|
|
33
|
+
if (!hit) return acc;
|
|
34
|
+
return {
|
|
35
|
+
total: acc.total + hit.credits,
|
|
36
|
+
unit: hit.unit ?? acc.unit
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
{ total: 0, unit: void 0 }
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
var creditsAmount = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 });
|
|
43
|
+
function formatCredits(value, unit) {
|
|
44
|
+
const amount = creditsAmount.format(Number.isFinite(value) ? value : 0);
|
|
45
|
+
if (!unit) return amount;
|
|
46
|
+
const label = value === 1 || unit.endsWith("s") ? unit : `${unit}s`;
|
|
47
|
+
return `${amount} ${label}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
readPartCredits,
|
|
52
|
+
messageCredits,
|
|
53
|
+
creditsForMessage,
|
|
54
|
+
sumSessionCredits,
|
|
55
|
+
formatCredits
|
|
56
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatCredits,
|
|
3
|
+
sumSessionCredits
|
|
4
|
+
} from "./chunk-JO7U4OSF.js";
|
|
5
|
+
|
|
6
|
+
// src/tui/context-view.ts
|
|
7
|
+
import { createElement, effect, insert, insertNode, setProp } from "@opentui/solid";
|
|
8
|
+
import { createMemo } from "solid-js";
|
|
9
|
+
function createContextView(api, sessionID) {
|
|
10
|
+
const theme = () => api.theme.current;
|
|
11
|
+
const messages = createMemo(() => api.state.session.messages(sessionID));
|
|
12
|
+
const usage = createMemo(() => {
|
|
13
|
+
const last = messages().findLast(
|
|
14
|
+
(item) => item.role === "assistant" && item.tokens.output > 0
|
|
15
|
+
);
|
|
16
|
+
if (!last) {
|
|
17
|
+
return {
|
|
18
|
+
tokens: 0,
|
|
19
|
+
percent: null
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const tokens = last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write;
|
|
23
|
+
const model = api.state.provider.find((item) => item.id === last.providerID)?.models[last.modelID];
|
|
24
|
+
return {
|
|
25
|
+
tokens,
|
|
26
|
+
percent: model?.limit.context ? Math.round(tokens / model.limit.context * 100) : null
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
const credits = createMemo(() => sumSessionCredits(messages(), (messageID) => api.state.part(messageID)));
|
|
30
|
+
const root = createElement("box");
|
|
31
|
+
insertNode(root, headerLine(theme));
|
|
32
|
+
insertNode(
|
|
33
|
+
root,
|
|
34
|
+
mutedLine(theme, () => `${usage().tokens.toLocaleString()} tokens`)
|
|
35
|
+
);
|
|
36
|
+
insertNode(
|
|
37
|
+
root,
|
|
38
|
+
mutedLine(theme, () => `${usage().percent ?? 0}% used`)
|
|
39
|
+
);
|
|
40
|
+
insertNode(
|
|
41
|
+
root,
|
|
42
|
+
mutedLine(theme, () => formatCredits(credits().total, credits().unit))
|
|
43
|
+
);
|
|
44
|
+
return root;
|
|
45
|
+
}
|
|
46
|
+
function headerLine(theme) {
|
|
47
|
+
const line = createElement("text");
|
|
48
|
+
effect(() => setProp(line, "fg", theme().text));
|
|
49
|
+
const bold = createElement("b");
|
|
50
|
+
insert(bold, "Context");
|
|
51
|
+
insertNode(line, bold);
|
|
52
|
+
return line;
|
|
53
|
+
}
|
|
54
|
+
function mutedLine(theme, content) {
|
|
55
|
+
const line = createElement("text");
|
|
56
|
+
effect(() => setProp(line, "fg", theme().textMuted));
|
|
57
|
+
insert(line, content);
|
|
58
|
+
return line;
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
createContextView
|
|
62
|
+
};
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// src/server.ts
|
|
2
|
+
var server = async (input) => ({
|
|
3
|
+
auth: {
|
|
4
|
+
provider: "kiro",
|
|
5
|
+
// Returned options are forwarded into createKiroAcp({...}). Relays each
|
|
6
|
+
// catalog model's limit.context into contextWindows keyed by api.id;
|
|
7
|
+
// zero/missing limits are skipped (SDK falls back to 1M).
|
|
8
|
+
loader: async (_getAuth, provider) => ({
|
|
9
|
+
cwd: input.directory ?? input.worktree,
|
|
10
|
+
agent: "opencode",
|
|
11
|
+
trustAllTools: true,
|
|
12
|
+
mcpTimeout: 45,
|
|
13
|
+
contextWindows: Object.fromEntries(
|
|
14
|
+
Object.values(provider.models).filter((m) => (m.limit?.context ?? 0) > 0).map((m) => [m.api.id, m.limit.context])
|
|
15
|
+
)
|
|
16
|
+
}),
|
|
17
|
+
methods: [
|
|
18
|
+
{
|
|
19
|
+
type: "oauth",
|
|
20
|
+
label: "Kiro CLI Login",
|
|
21
|
+
async authorize() {
|
|
22
|
+
const { verifyAuth } = await import("kiro-acp-ai-provider");
|
|
23
|
+
const status = verifyAuth();
|
|
24
|
+
if (!status.installed)
|
|
25
|
+
throw new Error(
|
|
26
|
+
"kiro-cli is not installed. Install it from https://kiro.dev/docs/cli/"
|
|
27
|
+
);
|
|
28
|
+
if (status.authenticated) {
|
|
29
|
+
return {
|
|
30
|
+
url: "",
|
|
31
|
+
instructions: "",
|
|
32
|
+
method: "auto",
|
|
33
|
+
async callback() {
|
|
34
|
+
return readToken(status.tokenPath);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const { execFile } = await import("child_process");
|
|
39
|
+
const child = execFile("kiro-cli", ["login"]);
|
|
40
|
+
return {
|
|
41
|
+
url: "",
|
|
42
|
+
instructions: "Complete Kiro authentication in the browser window that just opened. Waiting for login...",
|
|
43
|
+
method: "auto",
|
|
44
|
+
async callback() {
|
|
45
|
+
const maxWait = 12e4;
|
|
46
|
+
const start = Date.now();
|
|
47
|
+
while (Date.now() - start < maxWait) {
|
|
48
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
49
|
+
const check = verifyAuth();
|
|
50
|
+
if (check.authenticated) {
|
|
51
|
+
child.kill();
|
|
52
|
+
return readToken(check.tokenPath);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
child.kill();
|
|
56
|
+
throw new Error(
|
|
57
|
+
"Kiro authentication timed out. Run `kiro-cli auth login` manually."
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
async function readToken(tokenPath) {
|
|
67
|
+
if (tokenPath) {
|
|
68
|
+
try {
|
|
69
|
+
const { readFileSync } = await import("fs");
|
|
70
|
+
const raw = JSON.parse(readFileSync(tokenPath, "utf8"));
|
|
71
|
+
return {
|
|
72
|
+
type: "success",
|
|
73
|
+
refresh: "",
|
|
74
|
+
access: raw.accessToken || "authenticated",
|
|
75
|
+
expires: raw.expiresAt ? new Date(raw.expiresAt).getTime() : Date.now() + 36e5
|
|
76
|
+
};
|
|
77
|
+
} catch {
|
|
78
|
+
return { type: "failed" };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
type: "success",
|
|
83
|
+
refresh: "",
|
|
84
|
+
access: "authenticated",
|
|
85
|
+
expires: Date.now() + 36e5
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
var server_default = { id: "kiro", server };
|
|
89
|
+
export {
|
|
90
|
+
server_default as default
|
|
91
|
+
};
|
package/dist/tui.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { TuiPlugin } from '@opencode-ai/plugin/tui';
|
|
2
|
+
|
|
3
|
+
/** Any part-like object. `object` (not `{ metadata?: unknown }`) so metadata-less SDK Part variants stay assignable. */
|
|
4
|
+
type CreditPart = object;
|
|
5
|
+
/** Minimal message shape; SDK `Message` is assignable. */
|
|
6
|
+
interface CreditMessage {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly role: string;
|
|
9
|
+
}
|
|
10
|
+
/** One part's credit metadata: the turn total plus the SDK-reported unit. */
|
|
11
|
+
interface PartCredits {
|
|
12
|
+
credits: number;
|
|
13
|
+
unit?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Session-wide rollup. `unit` stays undefined until metadata reports one. */
|
|
16
|
+
interface SessionCredits {
|
|
17
|
+
total: number;
|
|
18
|
+
unit?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Read `{ kiro: { credits, creditsUnit } }` from a part; only finite numeric credits count. */
|
|
21
|
+
declare function readPartCredits(part: CreditPart): PartCredits | undefined;
|
|
22
|
+
/** One message's credits, deduped by the last-carrier-wins rule (parts never summed); unit from the most recent carrier. */
|
|
23
|
+
declare function messageCredits(parts: ReadonlyArray<CreditPart>): PartCredits | undefined;
|
|
24
|
+
/** Per-message credit total, or undefined when no part carries credits. */
|
|
25
|
+
declare function creditsForMessage(parts: ReadonlyArray<CreditPart>): number | undefined;
|
|
26
|
+
/** Sum per-message totals across a session's assistant messages (one value each); unit from the most recent carrier. */
|
|
27
|
+
declare function sumSessionCredits(messages: ReadonlyArray<CreditMessage>, partsByMessage: (messageID: string) => ReadonlyArray<CreditPart>): SessionCredits;
|
|
28
|
+
/** Render credits with the unit, e.g. "12.5 credits", "1 credit". Unit is naively pluralized unless it ends in "s"; with no unit, only the number renders. */
|
|
29
|
+
declare function formatCredits(value: number, unit?: string): string;
|
|
30
|
+
|
|
31
|
+
declare const _default: {
|
|
32
|
+
id: string;
|
|
33
|
+
tui: TuiPlugin;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export { type CreditMessage, type CreditPart, type PartCredits, type SessionCredits, creditsForMessage, _default as default, formatCredits, messageCredits, readPartCredits, sumSessionCredits };
|
package/dist/tui.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
creditsForMessage,
|
|
3
|
+
formatCredits,
|
|
4
|
+
messageCredits,
|
|
5
|
+
readPartCredits,
|
|
6
|
+
sumSessionCredits
|
|
7
|
+
} from "./chunk-JO7U4OSF.js";
|
|
8
|
+
|
|
9
|
+
// src/tui.ts
|
|
10
|
+
var tui = async (api) => {
|
|
11
|
+
const { createContextView } = await import("./context-view-CGBZAYM5.js");
|
|
12
|
+
api.slots.register({
|
|
13
|
+
order: 100,
|
|
14
|
+
slots: {
|
|
15
|
+
sidebar_content(_ctx, props) {
|
|
16
|
+
return createContextView(api, props.session_id);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
var tui_default = { id: "opencode-kiro", tui };
|
|
22
|
+
export {
|
|
23
|
+
creditsForMessage,
|
|
24
|
+
tui_default as default,
|
|
25
|
+
formatCredits,
|
|
26
|
+
messageCredits,
|
|
27
|
+
readPartCredits,
|
|
28
|
+
sumSessionCredits
|
|
29
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-kiro",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The ACP-compliant Kiro plugin for opencode: auth via the official kiro-cli login, 12 Kiro models through the Agent Client Protocol, and TUI credits display",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Nacho F. Lizaur (https://github.com/NachoFLizaur)",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/NachoFLizaur/opencode-kiro.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/NachoFLizaur/opencode-kiro/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/NachoFLizaur/opencode-kiro#readme",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
"./server": {
|
|
18
|
+
"types": "./dist/server.d.ts",
|
|
19
|
+
"default": "./dist/server.js"
|
|
20
|
+
},
|
|
21
|
+
"./tui": {
|
|
22
|
+
"types": "./dist/tui.d.ts",
|
|
23
|
+
"default": "./dist/tui.js"
|
|
24
|
+
},
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsup",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"prepublishOnly": "npm run build"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=20",
|
|
39
|
+
"opencode": ">=1.16.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@opencode-ai/plugin": "*"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"kiro-acp-ai-provider": "^2.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@opencode-ai/plugin": "1.16.2",
|
|
49
|
+
"@opentui/solid": "^0.3.4",
|
|
50
|
+
"@types/node": "^20.19.42",
|
|
51
|
+
"solid-js": "^1.9.12",
|
|
52
|
+
"tsup": "^8.0.0",
|
|
53
|
+
"typescript": "^5.7.0",
|
|
54
|
+
"vitest": "^4.1.8"
|
|
55
|
+
},
|
|
56
|
+
"keywords": [
|
|
57
|
+
"opencode",
|
|
58
|
+
"opencode-plugin",
|
|
59
|
+
"kiro",
|
|
60
|
+
"kiro-cli",
|
|
61
|
+
"acp",
|
|
62
|
+
"agent-client-protocol",
|
|
63
|
+
"amazon-q",
|
|
64
|
+
"aws"
|
|
65
|
+
]
|
|
66
|
+
}
|