opencode-kiro 0.1.2 → 0.1.4
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 +23 -13
- package/dist/{chunk-JHCHA2UV.js → chunk-MQVMKLNA.js} +14 -1
- package/dist/{context-view-3MFY3ASR.js → context-view-2KKFHTQV.js} +5 -8
- package/dist/{credits-chip-view-SKNVCWRO.js → credits-chip-view-RL5JAEZ5.js} +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +18 -24
- package/dist/tui.d.ts +15 -1
- package/dist/tui.js +5 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -157,9 +157,17 @@ image-capable models receive it as an attachment.
|
|
|
157
157
|
## Credits in the sidebar
|
|
158
158
|
|
|
159
159
|
Kiro is subscription-metered: requests consume **credits**, and the dollar cost
|
|
160
|
-
opencode normally displays is always $0.00. The plugin therefore
|
|
161
|
-
**full replacement** for the built-in sidebar context box, showing
|
|
162
|
-
percentage, and
|
|
160
|
+
opencode normally displays for Kiro turns is always $0.00. The plugin therefore
|
|
161
|
+
registers a **full replacement** for the built-in sidebar context box, showing
|
|
162
|
+
tokens, context percentage, and a spend line.
|
|
163
|
+
|
|
164
|
+
The spend line adapts to what the session actually used:
|
|
165
|
+
|
|
166
|
+
- **Dollars only** (no Kiro turns): the builtin's exact `$X.XX spent` line, so
|
|
167
|
+
non-Kiro sessions look identical to the built-in box.
|
|
168
|
+
- **Credits only** (Kiro turns, no dollar cost): a single `N credits` line.
|
|
169
|
+
- **Both** (the session used a dollar-based model AND Kiro in one session): two
|
|
170
|
+
stacked lines, `$X.XX spent` then `N credits`, so neither figure is hidden.
|
|
163
171
|
|
|
164
172
|
Disable the builtin box in `tui.json` so only the replacement renders:
|
|
165
173
|
|
|
@@ -174,19 +182,21 @@ one plus the plugin's). The credits value and its unit come from provider metada
|
|
|
174
182
|
emitted by the SDK (kiro-cli reports the unit); nothing is hardcoded client-side.
|
|
175
183
|
|
|
176
184
|
Trade-off: the replacement box applies to **every** session and disabling the builtin
|
|
177
|
-
is global
|
|
178
|
-
sessions
|
|
179
|
-
the
|
|
185
|
+
is global. The replacement reproduces the builtin `$X.XX spent` line for non-Kiro
|
|
186
|
+
sessions (and stacks it above credits when a session used both), so mixed-provider
|
|
187
|
+
users keep dollar cost in the sidebar; if you prefer the original built-in box, leave
|
|
188
|
+
it enabled at the cost of the duplicate box.
|
|
180
189
|
|
|
181
190
|
## Known limitation (read this)
|
|
182
191
|
|
|
183
|
-
**Credits
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
192
|
+
**Credits render in the TUI only.** Two TUI surfaces show them: the sidebar context
|
|
193
|
+
box (above) and the input/prompt meta row chip (`session_prompt_right`), which sits
|
|
194
|
+
beside the host's `$` cost chip. Every other cost surface (ACP clients, the web app,
|
|
195
|
+
desktop, web share pages, and CLI cost output) shows $0.00 for Kiro sessions. The
|
|
196
|
+
models.dev catalog declares Kiro's per-token `cost` as 0 (it is a subscription-metered
|
|
197
|
+
provider with no per-token pricing), so opencode core computes $0.00 everywhere it
|
|
198
|
+
renders dollar cost. That is expected, not a defect. A cross-surface credits display
|
|
199
|
+
would require opencode core changes and is intentionally out of scope for this plugin.
|
|
190
200
|
|
|
191
201
|
## How it works
|
|
192
202
|
|
|
@@ -47,11 +47,24 @@ function formatCredits(value, unit) {
|
|
|
47
47
|
const label = value === 1 || unit.endsWith("s") ? unit : `${unit}s`;
|
|
48
48
|
return `${amount} ${label}`;
|
|
49
49
|
}
|
|
50
|
+
var money = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
|
|
51
|
+
function spendLines(input) {
|
|
52
|
+
const { cost, credits } = input;
|
|
53
|
+
const dollars = money.format(Number.isFinite(cost) ? cost : 0);
|
|
54
|
+
if (credits.present && cost > 0) {
|
|
55
|
+
return [`${dollars} spent`, formatCredits(credits.total, credits.unit)];
|
|
56
|
+
}
|
|
57
|
+
if (credits.present) {
|
|
58
|
+
return [formatCredits(credits.total, credits.unit)];
|
|
59
|
+
}
|
|
60
|
+
return [`${dollars} spent`];
|
|
61
|
+
}
|
|
50
62
|
|
|
51
63
|
export {
|
|
52
64
|
readPartCredits,
|
|
53
65
|
messageCredits,
|
|
54
66
|
creditsForMessage,
|
|
55
67
|
sumSessionCredits,
|
|
56
|
-
formatCredits
|
|
68
|
+
formatCredits,
|
|
69
|
+
spendLines
|
|
57
70
|
};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
spendLines,
|
|
3
3
|
sumSessionCredits
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MQVMKLNA.js";
|
|
5
5
|
|
|
6
6
|
// src/tui/context-view.ts
|
|
7
7
|
import { createElement, effect, insert, insertNode, setProp } from "@opentui/solid";
|
|
8
8
|
import { createMemo } from "solid-js";
|
|
9
|
-
var money = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
|
|
10
9
|
function createContextView(api, sessionID) {
|
|
11
10
|
const theme = () => api.theme.current;
|
|
12
11
|
const messages = createMemo(() => api.state.session.messages(sessionID));
|
|
@@ -39,12 +38,10 @@ function createContextView(api, sessionID) {
|
|
|
39
38
|
root,
|
|
40
39
|
mutedLine(theme, () => `${usage().percent ?? 0}% used`)
|
|
41
40
|
);
|
|
42
|
-
|
|
41
|
+
const costLines = createMemo(() => spendLines({ cost: cost(), credits: credits() }));
|
|
42
|
+
insert(
|
|
43
43
|
root,
|
|
44
|
-
mutedLine(
|
|
45
|
-
theme,
|
|
46
|
-
() => credits().present ? formatCredits(credits().total, credits().unit) : `${money.format(cost())} spent`
|
|
47
|
-
)
|
|
44
|
+
() => costLines().map((line) => mutedLine(theme, () => line))
|
|
48
45
|
);
|
|
49
46
|
return root;
|
|
50
47
|
}
|
package/dist/server.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ declare function readToken(tokenPath: string | undefined): Promise<{
|
|
|
9
9
|
} | {
|
|
10
10
|
type: "failed";
|
|
11
11
|
}>;
|
|
12
|
-
declare function notifyIfTokenExpired(client: PluginInput["client"] | undefined
|
|
12
|
+
declare function notifyIfTokenExpired(client: PluginInput["client"] | undefined): Promise<void>;
|
|
13
13
|
declare const KiroAuthPlugin: Plugin;
|
|
14
14
|
declare const _default: {
|
|
15
15
|
id: string;
|
package/dist/server.js
CHANGED
|
@@ -4,7 +4,7 @@ import { dirname, join } from "path";
|
|
|
4
4
|
var KIRO_PLUGIN_NAME = "opencode-kiro";
|
|
5
5
|
var SIDEBAR_PLUGIN_ID = "internal:sidebar-context";
|
|
6
6
|
var server = async (input) => {
|
|
7
|
-
await notifyIfTokenExpired(input.client
|
|
7
|
+
await notifyIfTokenExpired(input.client);
|
|
8
8
|
const tuiPath = tuiConfigPath();
|
|
9
9
|
const alreadyConfigured = isSidebarConfigured(await readTuiConfig(tuiPath));
|
|
10
10
|
const prompts = alreadyConfigured ? [] : [
|
|
@@ -62,7 +62,9 @@ var server = async (input) => {
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
const { execFile } = await import("child_process");
|
|
65
|
-
const child = execFile("kiro-cli", ["login"]
|
|
65
|
+
const child = execFile("kiro-cli", ["login"], {
|
|
66
|
+
shell: process.platform === "win32"
|
|
67
|
+
});
|
|
66
68
|
return {
|
|
67
69
|
url: "",
|
|
68
70
|
instructions: "Complete Kiro authentication in the browser window that just opened. Waiting for login...",
|
|
@@ -104,36 +106,28 @@ async function readKiroTokenFile(tokenPath) {
|
|
|
104
106
|
return void 0;
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
|
-
function tokenExpiresAtMs(token) {
|
|
108
|
-
const raw = token?.expiresAt;
|
|
109
|
-
if (typeof raw === "number") return raw;
|
|
110
|
-
if (typeof raw === "string") return Date.parse(raw);
|
|
111
|
-
return NaN;
|
|
112
|
-
}
|
|
113
109
|
async function readToken(tokenPath) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
return { type: "failed" };
|
|
120
|
-
}
|
|
121
|
-
const access = typeof token.accessToken === "string" ? token.accessToken : "";
|
|
122
|
-
const refresh = typeof token.refreshToken === "string" ? token.refreshToken : "";
|
|
110
|
+
const { verifyAuth } = await import("kiro-acp-ai-provider");
|
|
111
|
+
if (!verifyAuth().authenticated) return { type: "failed" };
|
|
112
|
+
const token = tokenPath ? await readKiroTokenFile(tokenPath) : void 0;
|
|
113
|
+
const access = typeof token?.accessToken === "string" ? token.accessToken : "";
|
|
114
|
+
const refresh = typeof token?.refreshToken === "string" ? token.refreshToken : "";
|
|
123
115
|
return {
|
|
124
116
|
type: "success",
|
|
125
117
|
refresh,
|
|
126
|
-
//
|
|
118
|
+
// real refresh when present, else ""
|
|
127
119
|
access: access || "authenticated",
|
|
128
|
-
|
|
120
|
+
// cosmetic; opencode-core only needs presence
|
|
121
|
+
// FUTURE expiry, refreshed every startup (server() re-runs each session) so
|
|
122
|
+
// opencode-core does not flag a logged-in user as expired. NOT the file value.
|
|
123
|
+
expires: Date.now() + 8 * 60 * 60 * 1e3
|
|
129
124
|
};
|
|
130
125
|
}
|
|
131
|
-
async function notifyIfTokenExpired(client
|
|
126
|
+
async function notifyIfTokenExpired(client) {
|
|
132
127
|
try {
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const message = "Kiro token expired or missing. Run 'kiro-cli login' to re-authenticate.";
|
|
128
|
+
const { verifyAuth } = await import("kiro-acp-ai-provider");
|
|
129
|
+
if (verifyAuth().authenticated) return;
|
|
130
|
+
const message = "Kiro is not logged in. Run 'kiro-cli login' to authenticate.";
|
|
137
131
|
console.warn(message);
|
|
138
132
|
void client?.tui?.showToast?.({ body: { message, variant: "warning" } })?.catch(() => {
|
|
139
133
|
});
|
package/dist/tui.d.ts
CHANGED
|
@@ -34,10 +34,24 @@ declare function creditsForMessage(parts: ReadonlyArray<CreditPart>): number | u
|
|
|
34
34
|
declare function sumSessionCredits(messages: ReadonlyArray<CreditMessage>, partsByMessage: (messageID: string) => ReadonlyArray<CreditPart>): SessionCredits;
|
|
35
35
|
/** 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. */
|
|
36
36
|
declare function formatCredits(value: number, unit?: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* The sidebar's muted cost lines, returned as an ARRAY (one entry per rendered
|
|
39
|
+
* muted row). Three display states:
|
|
40
|
+
* - BOTH (credits present AND a non-zero dollar cost): TWO stacked lines
|
|
41
|
+
* ["$X.XX spent", "N credits"]
|
|
42
|
+
* - credits only (credits present, dollar cost 0): ["N credits"] (Kiro-only)
|
|
43
|
+
* - dollars only (no credits): ["$X.XX spent"] (also ["$0.00 spent"] when empty)
|
|
44
|
+
* The matrix keys off `credits.present` (a real 0-credit Kiro turn is present)
|
|
45
|
+
* and `cost > 0`, never off `credits.total` alone.
|
|
46
|
+
*/
|
|
47
|
+
declare function spendLines(input: {
|
|
48
|
+
cost: number;
|
|
49
|
+
credits: SessionCredits;
|
|
50
|
+
}): string[];
|
|
37
51
|
|
|
38
52
|
declare const _default: {
|
|
39
53
|
id: string;
|
|
40
54
|
tui: TuiPlugin;
|
|
41
55
|
};
|
|
42
56
|
|
|
43
|
-
export { type CreditMessage, type CreditPart, type PartCredits, type SessionCredits, creditsForMessage, _default as default, formatCredits, messageCredits, readPartCredits, sumSessionCredits };
|
|
57
|
+
export { type CreditMessage, type CreditPart, type PartCredits, type SessionCredits, creditsForMessage, _default as default, formatCredits, messageCredits, readPartCredits, spendLines, sumSessionCredits };
|
package/dist/tui.js
CHANGED
|
@@ -3,13 +3,14 @@ import {
|
|
|
3
3
|
formatCredits,
|
|
4
4
|
messageCredits,
|
|
5
5
|
readPartCredits,
|
|
6
|
+
spendLines,
|
|
6
7
|
sumSessionCredits
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-MQVMKLNA.js";
|
|
8
9
|
|
|
9
10
|
// src/tui.ts
|
|
10
11
|
var tui = async (api) => {
|
|
11
|
-
const { createContextView } = await import("./context-view-
|
|
12
|
-
const { createCreditsChipView } = await import("./credits-chip-view-
|
|
12
|
+
const { createContextView } = await import("./context-view-2KKFHTQV.js");
|
|
13
|
+
const { createCreditsChipView } = await import("./credits-chip-view-RL5JAEZ5.js");
|
|
13
14
|
api.slots.register({
|
|
14
15
|
order: 100,
|
|
15
16
|
slots: {
|
|
@@ -29,5 +30,6 @@ export {
|
|
|
29
30
|
formatCredits,
|
|
30
31
|
messageCredits,
|
|
31
32
|
readPartCredits,
|
|
33
|
+
spendLines,
|
|
32
34
|
sumSessionCredits
|
|
33
35
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-kiro",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
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
5
|
"license": "MIT",
|
|
6
6
|
"author": "Nacho F. Lizaur (https://github.com/NachoFLizaur)",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@opencode-ai/plugin": "*"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"kiro-acp-ai-provider": "^2.0.
|
|
45
|
+
"kiro-acp-ai-provider": "^2.0.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@opencode-ai/plugin": "1.16.2",
|