@javargasm/opencode-kiro-auth 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/README.md ADDED
@@ -0,0 +1,190 @@
1
+ # opencode-kiro
2
+
3
+ Kiro provider plugin for [OpenCode](https://opencode.ai). Authenticates via AWS Builder ID or IAM Identity Center and exposes all Kiro models through an Anthropic-compatible local gateway.
4
+
5
+ ## Features
6
+
7
+ - **AWS Builder ID / IAM Identity Center** — OAuth device-code login with automatic token refresh
8
+ - **Dynamic model discovery** — fetches available models from the Kiro API at runtime; falls back to a curated static catalog
9
+ - **Local Anthropic gateway** — translates Anthropic Messages API requests to Kiro's CodeWhisperer streaming protocol
10
+ - **Adaptive thinking** — maps reasoning effort levels (`low` → `max`) through the `output_config.effort` parameter
11
+ - **Multi-region** — supports `us-east-1` and `eu-central-1` Kiro API regions with automatic SSO region mapping
12
+ - **Zero external dependencies** — self-contained plugin; no runtime deps beyond the OpenCode SDK
13
+
14
+ ## Supported Models
15
+
16
+ | Model | Reasoning | Context | Effort Levels |
17
+ |:---|:---:|:---:|:---|
18
+ | Claude Fable 5 | ✅ | 1M | low, medium, high, xhigh, max |
19
+ | Claude Opus 4.8 | ✅ | 1M | low, medium, high, xhigh, max |
20
+ | Claude Opus 4.7 | ✅ | 1M | low, medium, high, xhigh, max |
21
+ | Claude Opus 4.6 | ✅ | 1M | low, medium, high, max |
22
+ | Claude Sonnet 4.6 | ✅ | 1M | low, medium, high, max |
23
+ | Claude Opus 4.5 | ✅ | 200K | — |
24
+ | Claude Sonnet 4.5 | ✅ | 200K | — |
25
+ | Claude Sonnet 4 | ✅ | 200K | — |
26
+ | Claude Haiku 4.5 | ❌ | 200K | — |
27
+ | DeepSeek 3.2 | ✅ | 128K | — |
28
+ | Kimi K2.5 | ✅ | 200K | — |
29
+ | MiniMax M2.1 / M2.5 | ❌ | 196K | — |
30
+ | GLM 4.7 / 4.7 Flash | ✅/❌ | 128K | — |
31
+ | Qwen3 Coder Next | ✅ | 256K | — |
32
+ | Qwen3 Coder 480B | ✅ | 128K | — |
33
+ | AGI Nova Beta | ✅ | 1M | — |
34
+
35
+ > Models without effort levels listed use Kiro's default reasoning behavior. Additional models may appear dynamically via the `ListAvailableModels` API.
36
+
37
+ ## Installation
38
+
39
+ ### From npm (recommended)
40
+
41
+ Add the plugin to your `opencode.json`:
42
+
43
+ ```json
44
+ {
45
+ "$schema": "https://opencode.ai/config.json",
46
+ "plugin": [
47
+ "opencode-kiro"
48
+ ]
49
+ }
50
+ ```
51
+
52
+ OpenCode will auto-install the package on startup.
53
+
54
+ ### From local source
55
+
56
+ 1. Clone the repository:
57
+
58
+ ```bash
59
+ git clone https://github.com/your-org/opencode-kiro.git
60
+ cd opencode-kiro
61
+ ```
62
+
63
+ 2. Install dependencies and build:
64
+
65
+ ```bash
66
+ bun install
67
+ bun run build
68
+ ```
69
+
70
+ 3. Register the plugin in your `opencode.json` using the absolute path to the built entry point:
71
+
72
+ ```json
73
+ {
74
+ "$schema": "https://opencode.ai/config.json",
75
+ "plugin": [
76
+ "/absolute/path/to/opencode-kiro/dist/index.js"
77
+ ]
78
+ }
79
+ ```
80
+
81
+ ## Authentication
82
+
83
+ Once the plugin is loaded, authenticate through the OpenCode TUI:
84
+
85
+ 1. Run `/connect` inside OpenCode
86
+ 2. Select **Kiro (Builder ID / IAM Identity Center)**
87
+ 3. Choose your login method:
88
+ - **Builder ID** — press Enter at the SSO URL prompt (default)
89
+ - **IAM Identity Center** — enter your organization's SSO Start URL (e.g. `https://mycompany.awsapps.com/start`) and optionally the SSO region
90
+ 4. Complete the browser-based authorization using the provided verification code
91
+ 5. Tokens are stored securely and refreshed automatically
92
+
93
+ ## Usage
94
+
95
+ After authentication, select any Kiro model in the OpenCode model picker. The plugin:
96
+
97
+ 1. Starts a local Anthropic-compatible gateway on a random port
98
+ 2. Registers all available models as OpenCode provider entries
99
+ 3. Routes requests through `@ai-sdk/anthropic` → local gateway → Kiro CodeWhisperer API
100
+
101
+ ### Selecting a model
102
+
103
+ Use the model picker in the OpenCode TUI or set a default in your `opencode.json`:
104
+
105
+ ```json
106
+ {
107
+ "model": "kiro/claude-opus-4-7"
108
+ }
109
+ ```
110
+
111
+ ### Reasoning effort
112
+
113
+ Models that support adaptive thinking accept effort levels through OpenCode's reasoning configuration. The plugin passes them 1:1 to Kiro's `output_config.effort` parameter:
114
+
115
+ - `low` — speed/cost optimized
116
+ - `medium` — balanced general-purpose
117
+ - `high` — default, best balance
118
+ - `xhigh` — complex multi-step tasks (Fable 5, Opus 4.7, 4.8)
119
+ - `max` — maximum reasoning depth (Fable 5, Opus 4.7, 4.8)
120
+
121
+ Not all models support every level — see the model table above for supported efforts per model.
122
+
123
+ ## Development
124
+
125
+ ### Prerequisites
126
+
127
+ - [Bun](https://bun.sh) ≥ 1.0
128
+ - TypeScript ≥ 5.0
129
+
130
+ ### Commands
131
+
132
+ ```bash
133
+ # Type-check + run tests
134
+ bun run check
135
+
136
+ # Type-check only
137
+ bun run typecheck
138
+
139
+ # Run tests
140
+ bun test
141
+
142
+ # Run tests in watch mode
143
+ bun test --watch
144
+
145
+ # Build for distribution
146
+ bun run build
147
+ ```
148
+
149
+ ### Project Structure
150
+
151
+ ```
152
+ src/
153
+ ├── index.ts # Plugin entry: auth hooks, model registration, gateway lifecycle
154
+ ├── types.ts # Local type definitions and runtime utilities
155
+ ├── server.ts # Bun.serve Anthropic gateway (Messages API → Kiro SSE)
156
+ ├── stream.ts # Kiro streaming orchestrator (request build, retry, event parsing)
157
+ ├── models.ts # Model catalog, region mapping, dynamic model discovery
158
+ ├── oauth.ts # OIDC device-code auth (Builder ID + Identity Center)
159
+ ├── transform.ts # Message format conversion (OpenCode ↔ Kiro wire format)
160
+ ├── thinking-parser.ts # Streaming <thinking> tag parser for inline reasoning
161
+ ├── event-parser.ts # Kiro JSON event stream parser
162
+ ├── kiro-defaults.ts # Static protocol constants (system seed, tool schemas)
163
+ ├── health.ts # Permanent error classification
164
+ ├── tokenizer.ts # Lightweight token estimation
165
+ └── debug.ts # Structured logging
166
+ test/
167
+ └── gateway.test.ts # Gateway integration tests (health, auth, streaming, non-streaming)
168
+ ```
169
+
170
+ ## Architecture
171
+
172
+ ```
173
+ ┌──────────────┐ ┌─────────────────────┐ ┌──────────────────┐
174
+ │ OpenCode │────▶│ Local Gateway │────▶│ Kiro API │
175
+ │ (@ai-sdk/ │ │ (Bun.serve) │ │ (CodeWhisperer │
176
+ │ anthropic) │◀────│ │◀────│ Streaming) │
177
+ │ │ SSE │ POST /v1/messages │ │ │
178
+ └──────────────┘ └─────────────────────┘ └──────────────────┘
179
+
180
+ │ Translates:
181
+ │ • Anthropic Messages → Kiro request body
182
+ │ • Kiro JSON events → Anthropic SSE events
183
+ │ • Handles retry, capacity, context truncation
184
+ ```
185
+
186
+ The gateway runs on `127.0.0.1` on a random port. It accepts standard Anthropic Messages API requests and translates them bidirectionally to Kiro's proprietary CodeWhisperer streaming protocol.
187
+
188
+ ## License
189
+
190
+ MIT
@@ -0,0 +1,22 @@
1
+ export interface RequestMetrics {
2
+ id: string;
3
+ timestamp: number;
4
+ model: string;
5
+ inputTokens: number;
6
+ outputTokens: number;
7
+ credits: number;
8
+ stream: boolean;
9
+ }
10
+ declare class DashboardStats {
11
+ private requests;
12
+ recordRequest(metrics: Omit<RequestMetrics, "timestamp">): void;
13
+ getStats(): {
14
+ totalRequests: number;
15
+ totalTokens: number;
16
+ totalCredits: number;
17
+ requests: RequestMetrics[];
18
+ };
19
+ }
20
+ export declare const stats: DashboardStats;
21
+ export {};
22
+ //# sourceMappingURL=dashboard-stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-stats.d.ts","sourceRoot":"","sources":["../src/dashboard-stats.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AAID,cAAM,cAAc;IAClB,OAAO,CAAC,QAAQ,CAAwB;IAExC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC;IAOxD,QAAQ;;;;;;CAYT;AAED,eAAO,MAAM,KAAK,gBAAuB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function getDashboardHtml(): string;
2
+ //# sourceMappingURL=dashboard-ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-ui.d.ts","sourceRoot":"","sources":["../src/dashboard-ui.ts"],"names":[],"mappings":"AAAA,wBAAgB,gBAAgB,IAAI,MAAM,CA8TzC"}
@@ -0,0 +1,12 @@
1
+ export type LogLevel = "error" | "warn" | "info" | "debug";
2
+ export declare const log: {
3
+ error: (msg: string, data?: unknown) => void;
4
+ warn: (msg: string, data?: unknown) => void;
5
+ info: (msg: string, data?: unknown) => void;
6
+ debug: (msg: string, data?: unknown) => void;
7
+ /** True when the current threshold includes `debug`. Use to avoid
8
+ * expensive serialization of payloads we won't log. */
9
+ isDebug: () => boolean;
10
+ };
11
+ export declare function previewChunk(s: string): string;
12
+ //# sourceMappingURL=debug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAgF3D,eAAO,MAAM,GAAG;iBACD,MAAM,SAAS,OAAO;gBACvB,MAAM,SAAS,OAAO;gBACtB,MAAM,SAAS,OAAO;iBACrB,MAAM,SAAS,OAAO;IACnC;4DACwD;;CAEzD,CAAC;AAWF,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAa9C"}
@@ -0,0 +1,61 @@
1
+ export type KiroStreamEvent = {
2
+ type: "content";
3
+ data: string;
4
+ } | {
5
+ type: "toolUse";
6
+ data: {
7
+ name: string;
8
+ toolUseId: string;
9
+ input: string;
10
+ stop?: boolean;
11
+ };
12
+ } | {
13
+ type: "toolUseInput";
14
+ data: {
15
+ input: string;
16
+ };
17
+ } | {
18
+ type: "toolUseStop";
19
+ data: {
20
+ stop: boolean;
21
+ };
22
+ } | {
23
+ type: "contextUsage";
24
+ data: {
25
+ contextUsagePercentage: number;
26
+ };
27
+ } | {
28
+ type: "reasoning";
29
+ data: {
30
+ text: string;
31
+ signature?: string;
32
+ };
33
+ } | {
34
+ type: "followupPrompt";
35
+ data: string;
36
+ } | {
37
+ type: "usage";
38
+ data: {
39
+ inputTokens?: number;
40
+ outputTokens?: number;
41
+ };
42
+ } | {
43
+ type: "error";
44
+ data: {
45
+ error: string;
46
+ message?: string;
47
+ };
48
+ } | {
49
+ type: "metering";
50
+ data: {
51
+ usage: number;
52
+ };
53
+ };
54
+ /** Find the matching `}` for the `{` at `start`. Returns -1 if incomplete. */
55
+ export declare function findJsonEnd(text: string, start: number): number;
56
+ export declare function parseKiroEventMulti(parsed: Record<string, unknown>): KiroStreamEvent[];
57
+ export declare function parseKiroEvents(buffer: string): {
58
+ events: KiroStreamEvent[];
59
+ remaining: string;
60
+ };
61
+ //# sourceMappingURL=event-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-parser.d.ts","sourceRoot":"","sources":["../src/event-parser.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAC7F;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE;QAAE,sBAAsB,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACxE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAElD,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA2B/D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,EAAE,CAqItF;AAoCD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,GACb;IAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAqElD"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Returns true if the error message indicates a permanent failure that
3
+ * will not resolve without user re-authentication.
4
+ */
5
+ export declare function isPermanentError(reason?: string): boolean;
6
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAGzD"}
@@ -0,0 +1,8 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ export declare const KiroPlugin: Plugin;
3
+ declare const _default: {
4
+ id: string;
5
+ server: Plugin;
6
+ };
7
+ export default _default;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAuB,MAAM,qBAAqB,CAAC;AAoBvE,eAAO,MAAM,UAAU,EAAE,MAiUxB,CAAC;;;;;AAEF,wBAGyB"}