@bogyie/opencode-kiro-plugin 0.2.12 → 0.2.14
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 +2 -2
- package/dist/auth.d.ts +3 -0
- package/dist/auth.js +27 -8
- package/dist/cli-transport.js +5 -11
- package/dist/kiro-rest-transport.d.ts +1 -0
- package/dist/kiro-rest-transport.js +17 -2
- package/dist/plugin.js +13 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,9 +38,9 @@ The plugin resolves credentials in this order:
|
|
|
38
38
|
3. Local Kiro CLI session token from the Kiro CLI SQLite store
|
|
39
39
|
4. `kiro-cli whoami` diagnostics for CLI session visibility
|
|
40
40
|
|
|
41
|
-
When no usable Kiro CLI session is available, choose
|
|
41
|
+
When no usable Kiro CLI session is available, open OpenCode's provider connector, choose Kiro, and select `Kiro CLI login`. The plugin starts `kiro-cli login --use-device-flow`, waits until the verification URL/device code is visible, returns them to the connector, and then waits until `kiro-cli whoami` succeeds. The same auth method also works from OpenCode's CLI provider connect flow.
|
|
42
42
|
|
|
43
|
-
Direct fetch mode uses a usable API key/token when one is configured; otherwise it reads the active Kiro CLI session token and calls Kiro's REST/EventStream endpoint directly. `cli-chat` mode uses the official `kiro-cli chat --no-interactive` surface and
|
|
43
|
+
The plugin does not start login during OpenCode startup. Direct fetch mode uses a usable API key/token when one is configured; otherwise it reads the active Kiro CLI session token and calls Kiro's REST/EventStream endpoint directly. If a model request fails because credentials are missing or rejected, the plugin starts one shared Kiro CLI login session and still returns `KIRO_AUTH_ERROR` for that failed request; retry after the login completes. `cli-chat` mode uses the official `kiro-cli chat --no-interactive` surface and applies the same login-on-auth-failure behavior. `acp` mode uses the official `kiro-cli acp` surface, but is still treated as an explicit backend while its real-world protocol behavior is validated across Kiro CLI versions.
|
|
44
44
|
|
|
45
45
|
Use the `kiro_status` plugin tool to inspect provider id, backend, region, auth method, and discovered model count. Secrets are redacted in diagnostics.
|
|
46
46
|
|
package/dist/auth.d.ts
CHANGED
|
@@ -32,7 +32,9 @@ export interface KiroCliSessionCredentialOptions {
|
|
|
32
32
|
}
|
|
33
33
|
export interface KiroLoginSession {
|
|
34
34
|
readonly url: string;
|
|
35
|
+
readonly code: string | undefined;
|
|
35
36
|
readonly instructions: string;
|
|
37
|
+
waitForPrompt(timeoutMs?: number): Promise<boolean>;
|
|
36
38
|
waitForAuth(runner?: CommandRunner): Promise<boolean>;
|
|
37
39
|
}
|
|
38
40
|
export declare const KIRO_LOGIN_URL = "https://view.awsapps.com/start";
|
|
@@ -42,6 +44,7 @@ export declare function runCommand(command: string, args: ReadonlyArray<string>,
|
|
|
42
44
|
export declare function regionFromProfileArn(profileArn: string | undefined): string | undefined;
|
|
43
45
|
export declare function readKiroCliSessionCredential(options?: KiroCliSessionCredentialOptions, runner?: CommandRunner): Promise<KiroCliSessionCredential | undefined>;
|
|
44
46
|
export declare function extractKiroLoginUrl(output: string): string;
|
|
47
|
+
export declare function extractKiroLoginCode(output: string): string | undefined;
|
|
45
48
|
export declare function startKiroCliLogin(spawner?: ProcessSpawner): KiroLoginSession;
|
|
46
49
|
export declare function startKiroCliLoginOnce(spawner?: ProcessSpawner): KiroLoginSession;
|
|
47
50
|
export declare function redacted(value: string | undefined): string | undefined;
|
package/dist/auth.js
CHANGED
|
@@ -101,6 +101,9 @@ function firstUrl(text) {
|
|
|
101
101
|
export function extractKiroLoginUrl(output) {
|
|
102
102
|
return firstUrl(output) ?? KIRO_LOGIN_URL;
|
|
103
103
|
}
|
|
104
|
+
export function extractKiroLoginCode(output) {
|
|
105
|
+
return /\b[A-Z0-9]{4}-[A-Z0-9]{4}\b/.exec(output)?.[0] ?? /\b[A-Z0-9]{8,}\b/.exec(output)?.[0];
|
|
106
|
+
}
|
|
104
107
|
export function startKiroCliLogin(spawner = (command, args) => spawn(command, [...args], { stdio: ["ignore", "pipe", "pipe"] })) {
|
|
105
108
|
const child = spawner("kiro-cli", ["login", "--use-device-flow"]);
|
|
106
109
|
let output = "";
|
|
@@ -120,15 +123,25 @@ export function startKiroCliLogin(spawner = (command, args) => spawn(command, [.
|
|
|
120
123
|
get url() {
|
|
121
124
|
return extractKiroLoginUrl(output);
|
|
122
125
|
},
|
|
126
|
+
get code() {
|
|
127
|
+
return extractKiroLoginCode(output);
|
|
128
|
+
},
|
|
123
129
|
get instructions() {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
const code = extractKiroLoginCode(output);
|
|
131
|
+
return code ? `Enter code: ${code}` : "Complete Kiro CLI login in your browser. This window will close automatically.";
|
|
132
|
+
},
|
|
133
|
+
async waitForPrompt(timeoutMs = 15_000) {
|
|
134
|
+
const deadline = Date.now() + timeoutMs;
|
|
135
|
+
while (Date.now() < deadline) {
|
|
136
|
+
if (extractKiroLoginCode(output))
|
|
137
|
+
return true;
|
|
138
|
+
if (firstUrl(output))
|
|
139
|
+
return true;
|
|
140
|
+
if (exited && exitCode !== 0)
|
|
141
|
+
return false;
|
|
142
|
+
await delay(100);
|
|
143
|
+
}
|
|
144
|
+
return Boolean(extractKiroLoginCode(output) || firstUrl(output));
|
|
132
145
|
},
|
|
133
146
|
async waitForAuth(runner = runCommand) {
|
|
134
147
|
const deadline = Date.now() + 10 * 60 * 1000;
|
|
@@ -153,9 +166,15 @@ export function startKiroCliLoginOnce(spawner = (command, args) => spawn(command
|
|
|
153
166
|
get url() {
|
|
154
167
|
return session.url;
|
|
155
168
|
},
|
|
169
|
+
get code() {
|
|
170
|
+
return session.code;
|
|
171
|
+
},
|
|
156
172
|
get instructions() {
|
|
157
173
|
return session.instructions;
|
|
158
174
|
},
|
|
175
|
+
waitForPrompt(timeoutMs) {
|
|
176
|
+
return session.waitForPrompt(timeoutMs);
|
|
177
|
+
},
|
|
159
178
|
async waitForAuth(runner) {
|
|
160
179
|
try {
|
|
161
180
|
return await session.waitForAuth(runner);
|
package/dist/cli-transport.js
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
import { runCommand, startKiroCliLoginOnce
|
|
1
|
+
import { runCommand, startKiroCliLoginOnce } from "./auth.js";
|
|
2
2
|
import { KiroPluginError } from "./errors.js";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
-
let lastLoginStartAt = 0;
|
|
5
|
-
function startKiroCliLoginOnce() {
|
|
6
|
-
const now = Date.now();
|
|
7
|
-
if (now - lastLoginStartAt < 30_000)
|
|
8
|
-
return;
|
|
9
|
-
lastLoginStartAt = now;
|
|
10
|
-
startSharedKiroCliLoginOnce();
|
|
11
|
-
}
|
|
12
4
|
export function promptForCli(request) {
|
|
13
5
|
const parts = [
|
|
14
6
|
request.system ? `System:\n${request.system}` : "",
|
|
@@ -98,7 +90,9 @@ export class KiroCliChatTransport {
|
|
|
98
90
|
constructor(options = {}) {
|
|
99
91
|
this.#runner = options.runner ?? runCommand;
|
|
100
92
|
this.#spawner = options.spawner ?? ((command, args) => spawn(command, [...args], { stdio: ["ignore", "pipe", "pipe"] }));
|
|
101
|
-
this.#loginStarter = options.loginStarter ??
|
|
93
|
+
this.#loginStarter = options.loginStarter ?? (() => {
|
|
94
|
+
startKiroCliLoginOnce();
|
|
95
|
+
});
|
|
102
96
|
this.#trustAllTools = options.trustAllTools === true;
|
|
103
97
|
this.#requestTimeoutMs = options.requestTimeoutMs ?? 120_000;
|
|
104
98
|
}
|
|
@@ -107,7 +101,7 @@ export class KiroCliChatTransport {
|
|
|
107
101
|
this.#loginStarter();
|
|
108
102
|
}
|
|
109
103
|
catch {
|
|
110
|
-
//
|
|
104
|
+
// Keep the original model-call auth error visible to OpenCode.
|
|
111
105
|
}
|
|
112
106
|
}
|
|
113
107
|
async generate(request) {
|
|
@@ -17,6 +17,7 @@ export type KiroRestFetch = (input: RequestInfo | URL, init?: RequestInit) => Pr
|
|
|
17
17
|
export interface KiroRestTransportDependencies {
|
|
18
18
|
readonly fetcher?: KiroRestFetch;
|
|
19
19
|
readonly credentialProvider?: KiroCredentialProvider;
|
|
20
|
+
readonly loginStarter?: () => void;
|
|
20
21
|
}
|
|
21
22
|
export declare function toKiroRestPayload(request: KiroGenerateRequest, profileArn?: string): Record<string, unknown>;
|
|
22
23
|
export declare class KiroRestTransport implements KiroTransport {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { KiroPluginError } from "./errors.js";
|
|
2
|
-
import { readKiroCliSessionCredential, regionFromProfileArn, } from "./auth.js";
|
|
2
|
+
import { readKiroCliSessionCredential, regionFromProfileArn, startKiroCliLoginOnce, } from "./auth.js";
|
|
3
3
|
const DEFAULT_USER_AGENT = "KiroIDE";
|
|
4
4
|
const DEFAULT_AGENT_MODE = "vibe";
|
|
5
5
|
const STREAMING_SDK_VERSION = "1.0.34";
|
|
@@ -313,16 +313,29 @@ export class KiroRestTransport {
|
|
|
313
313
|
#options;
|
|
314
314
|
#fetcher;
|
|
315
315
|
#credentialProvider;
|
|
316
|
+
#loginStarter;
|
|
316
317
|
constructor(options, dependencies = {}) {
|
|
317
318
|
this.#options = options;
|
|
318
319
|
this.#fetcher = dependencies.fetcher ?? fetch;
|
|
319
320
|
this.#credentialProvider = dependencies.credentialProvider ?? (() => readKiroCliSessionCredential());
|
|
321
|
+
this.#loginStarter = dependencies.loginStarter ?? (() => {
|
|
322
|
+
startKiroCliLoginOnce();
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
#startLoginAfterAuthFailure() {
|
|
326
|
+
try {
|
|
327
|
+
this.#loginStarter();
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
// Keep the original model-call auth error visible to OpenCode.
|
|
331
|
+
}
|
|
320
332
|
}
|
|
321
333
|
async #credentials() {
|
|
322
334
|
const session = this.#options.accessToken ? undefined : await this.#credentialProvider();
|
|
323
335
|
const accessToken = this.#options.accessToken ?? session?.accessToken;
|
|
324
336
|
if (!accessToken) {
|
|
325
|
-
|
|
337
|
+
this.#startLoginAfterAuthFailure();
|
|
338
|
+
throw new KiroPluginError("No Kiro API token or Kiro CLI session token found. Connect Kiro from OpenCode's provider connector or run `kiro-cli login`.", "KIRO_AUTH_ERROR", 401);
|
|
326
339
|
}
|
|
327
340
|
const profileArn = this.#options.profileArn ?? session?.profileArn;
|
|
328
341
|
return {
|
|
@@ -355,6 +368,8 @@ export class KiroRestTransport {
|
|
|
355
368
|
lastError = new KiroPluginError(message, code, response.status);
|
|
356
369
|
if (response.status === 429 || response.status >= 500)
|
|
357
370
|
continue;
|
|
371
|
+
if (code === "KIRO_AUTH_ERROR")
|
|
372
|
+
this.#startLoginAfterAuthFailure();
|
|
358
373
|
throw lastError;
|
|
359
374
|
}
|
|
360
375
|
if (!response.body)
|
package/dist/plugin.js
CHANGED
|
@@ -171,13 +171,25 @@ export function createKiroPlugin() {
|
|
|
171
171
|
label: "Kiro CLI login",
|
|
172
172
|
authorize: async () => {
|
|
173
173
|
const session = startKiroCliLoginOnce();
|
|
174
|
+
await session.waitForPrompt();
|
|
174
175
|
return {
|
|
175
176
|
url: session.url,
|
|
176
177
|
instructions: session.instructions,
|
|
177
178
|
method: "auto",
|
|
178
179
|
callback: async () => {
|
|
179
180
|
const authenticated = await session.waitForAuth();
|
|
180
|
-
|
|
181
|
+
if (!authenticated)
|
|
182
|
+
return { type: "failed" };
|
|
183
|
+
const diagnostics = await detectAuth().catch(() => undefined);
|
|
184
|
+
return {
|
|
185
|
+
type: "success",
|
|
186
|
+
key: "kiro-plugin-local-transport",
|
|
187
|
+
metadata: {
|
|
188
|
+
source: "kiro-cli",
|
|
189
|
+
...(diagnostics?.account ? { account: diagnostics.account } : {}),
|
|
190
|
+
...(diagnostics?.region ? { region: diagnostics.region } : {}),
|
|
191
|
+
},
|
|
192
|
+
};
|
|
181
193
|
},
|
|
182
194
|
};
|
|
183
195
|
},
|