@bogyie/opencode-kiro-plugin 0.2.7 → 0.2.8
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 -0
- package/dist/auth.d.ts +10 -0
- package/dist/auth.js +54 -0
- package/dist/cli-transport.d.ts +1 -0
- package/dist/cli-transport.js +23 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.js +17 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,6 +37,8 @@ The plugin resolves credentials in this order:
|
|
|
37
37
|
2. OpenCode auth input for provider `kiro`
|
|
38
38
|
3. `kiro-cli whoami` diagnostics for CLI session visibility
|
|
39
39
|
|
|
40
|
+
When no usable Kiro CLI session is available, choose the `Kiro CLI login` auth method in OpenCode. The plugin starts `kiro-cli login --use-device-flow`, opens the browser through the official CLI login flow, and waits until `kiro-cli whoami` succeeds.
|
|
41
|
+
|
|
40
42
|
Direct fetch mode requires an API key/token usable by the Kiro/CodeWhisperer client. `cli-chat` mode uses the official `kiro-cli chat --no-interactive` surface and depends on the local Kiro CLI login state. `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.
|
|
41
43
|
|
|
42
44
|
Use the `kiro_status` plugin tool to inspect provider id, backend, region, auth method, and discovered model count. Secrets are redacted in diagnostics.
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
1
2
|
export type AuthMethod = "api-key" | "cli-session" | "none";
|
|
2
3
|
export interface AuthDiagnostics {
|
|
3
4
|
readonly authenticated: boolean;
|
|
@@ -16,7 +17,16 @@ export interface CommandRunOptions {
|
|
|
16
17
|
readonly timeoutMs?: number;
|
|
17
18
|
}
|
|
18
19
|
export type CommandRunner = (command: string, args: ReadonlyArray<string>, options?: CommandRunOptions) => Promise<CommandResult>;
|
|
20
|
+
export type ProcessSpawner = (command: string, args: ReadonlyArray<string>) => ChildProcess;
|
|
21
|
+
export interface KiroLoginSession {
|
|
22
|
+
readonly url: string;
|
|
23
|
+
readonly instructions: string;
|
|
24
|
+
waitForAuth(runner?: CommandRunner): Promise<boolean>;
|
|
25
|
+
}
|
|
26
|
+
export declare const KIRO_LOGIN_URL = "https://view.awsapps.com/start";
|
|
19
27
|
export declare function runCommand(command: string, args: ReadonlyArray<string>, options?: CommandRunOptions): Promise<CommandResult>;
|
|
28
|
+
export declare function extractKiroLoginUrl(output: string): string;
|
|
29
|
+
export declare function startKiroCliLogin(spawner?: ProcessSpawner): KiroLoginSession;
|
|
20
30
|
export declare function redacted(value: string | undefined): string | undefined;
|
|
21
31
|
export declare function detectAuth(env?: NodeJS.ProcessEnv, runner?: CommandRunner): Promise<AuthDiagnostics>;
|
|
22
32
|
export declare function resolveApiKey(auth: () => Promise<{
|
package/dist/auth.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
2
3
|
import { promisify } from "node:util";
|
|
3
4
|
const execFileAsync = promisify(execFile);
|
|
5
|
+
export const KIRO_LOGIN_URL = "https://view.awsapps.com/start";
|
|
4
6
|
export async function runCommand(command, args, options = {}) {
|
|
5
7
|
try {
|
|
6
8
|
const result = await execFileAsync(command, [...args], { timeout: options.timeoutMs ?? 5000 });
|
|
@@ -16,6 +18,58 @@ export async function runCommand(command, args, options = {}) {
|
|
|
16
18
|
};
|
|
17
19
|
}
|
|
18
20
|
}
|
|
21
|
+
function delay(ms) {
|
|
22
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
23
|
+
}
|
|
24
|
+
function firstUrl(text) {
|
|
25
|
+
return /https?:\/\/[^\s"'<>]+/.exec(text)?.[0];
|
|
26
|
+
}
|
|
27
|
+
export function extractKiroLoginUrl(output) {
|
|
28
|
+
return firstUrl(output) ?? KIRO_LOGIN_URL;
|
|
29
|
+
}
|
|
30
|
+
export function startKiroCliLogin(spawner = (command, args) => spawn(command, [...args], { stdio: ["ignore", "pipe", "pipe"] })) {
|
|
31
|
+
const child = spawner("kiro-cli", ["login", "--use-device-flow"]);
|
|
32
|
+
let output = "";
|
|
33
|
+
let exited = false;
|
|
34
|
+
let exitCode = null;
|
|
35
|
+
child.stdout?.on("data", (chunk) => {
|
|
36
|
+
output += chunk.toString("utf8");
|
|
37
|
+
});
|
|
38
|
+
child.stderr?.on("data", (chunk) => {
|
|
39
|
+
output += chunk.toString("utf8");
|
|
40
|
+
});
|
|
41
|
+
child.on("exit", (code) => {
|
|
42
|
+
exited = true;
|
|
43
|
+
exitCode = code;
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
get url() {
|
|
47
|
+
return extractKiroLoginUrl(output);
|
|
48
|
+
},
|
|
49
|
+
get instructions() {
|
|
50
|
+
const url = extractKiroLoginUrl(output);
|
|
51
|
+
const code = /\b[A-Z0-9]{4}-[A-Z0-9]{4}\b/.exec(output)?.[0] ?? /\b[A-Z0-9]{8,}\b/.exec(output)?.[0];
|
|
52
|
+
return [
|
|
53
|
+
"Complete Kiro CLI login in the browser.",
|
|
54
|
+
`URL: ${url}`,
|
|
55
|
+
...(code ? [`Code: ${code}`] : []),
|
|
56
|
+
"The plugin will continue after `kiro-cli whoami` succeeds.",
|
|
57
|
+
].join("\n");
|
|
58
|
+
},
|
|
59
|
+
async waitForAuth(runner = runCommand) {
|
|
60
|
+
const deadline = Date.now() + 10 * 60 * 1000;
|
|
61
|
+
while (Date.now() < deadline) {
|
|
62
|
+
const auth = await detectAuth(process.env, runner);
|
|
63
|
+
if (auth.authenticated)
|
|
64
|
+
return true;
|
|
65
|
+
if (exited && exitCode !== 0)
|
|
66
|
+
return false;
|
|
67
|
+
await delay(2000);
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
19
73
|
export function redacted(value) {
|
|
20
74
|
if (!value)
|
|
21
75
|
return undefined;
|
package/dist/cli-transport.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { type ChildProcess } from "node:child_process";
|
|
|
6
6
|
export interface CliChatTransportOptions {
|
|
7
7
|
readonly runner?: CommandRunner;
|
|
8
8
|
readonly spawner?: CliChatSpawner;
|
|
9
|
+
readonly loginStarter?: () => void;
|
|
9
10
|
readonly trustAllTools?: boolean;
|
|
10
11
|
readonly requestTimeoutMs?: number;
|
|
11
12
|
}
|
package/dist/cli-transport.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import { runCommand } from "./auth.js";
|
|
1
|
+
import { runCommand, startKiroCliLogin } 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
|
+
startKiroCliLogin();
|
|
11
|
+
}
|
|
4
12
|
export function promptForCli(request) {
|
|
5
13
|
const parts = [
|
|
6
14
|
request.system ? `System:\n${request.system}` : "",
|
|
@@ -84,20 +92,32 @@ class AsyncQueue {
|
|
|
84
92
|
export class KiroCliChatTransport {
|
|
85
93
|
#runner;
|
|
86
94
|
#spawner;
|
|
95
|
+
#loginStarter;
|
|
87
96
|
#trustAllTools;
|
|
88
97
|
#requestTimeoutMs;
|
|
89
98
|
constructor(options = {}) {
|
|
90
99
|
this.#runner = options.runner ?? runCommand;
|
|
91
100
|
this.#spawner = options.spawner ?? ((command, args) => spawn(command, [...args], { stdio: ["ignore", "pipe", "pipe"] }));
|
|
101
|
+
this.#loginStarter = options.loginStarter ?? startKiroCliLoginOnce;
|
|
92
102
|
this.#trustAllTools = options.trustAllTools === true;
|
|
93
103
|
this.#requestTimeoutMs = options.requestTimeoutMs ?? 120_000;
|
|
94
104
|
}
|
|
105
|
+
#startLoginAfterAuthFailure() {
|
|
106
|
+
try {
|
|
107
|
+
this.#loginStarter();
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Preserve the original auth error. Login launch failures are diagnostic-only here.
|
|
111
|
+
}
|
|
112
|
+
}
|
|
95
113
|
async generate(request) {
|
|
96
114
|
const result = await this.#runner("kiro-cli", cliChatArgs(request, { trustAllTools: this.#trustAllTools }), {
|
|
97
115
|
timeoutMs: this.#requestTimeoutMs,
|
|
98
116
|
});
|
|
99
117
|
if (!result.ok) {
|
|
100
118
|
const authError = result.stderr.toLowerCase().includes("not logged in");
|
|
119
|
+
if (authError)
|
|
120
|
+
this.#startLoginAfterAuthFailure();
|
|
101
121
|
throw new KiroPluginError(result.stderr.trim() || "kiro-cli chat failed", authError ? "KIRO_AUTH_ERROR" : "KIRO_CLI_FAILED", authError ? 401 : 502);
|
|
102
122
|
}
|
|
103
123
|
const text = sanitizeCliChatOutput(result.stdout);
|
|
@@ -149,6 +169,8 @@ export class KiroCliChatTransport {
|
|
|
149
169
|
return;
|
|
150
170
|
}
|
|
151
171
|
const authError = stderr.toLowerCase().includes("not logged in");
|
|
172
|
+
if (authError)
|
|
173
|
+
this.#startLoginAfterAuthFailure();
|
|
152
174
|
queue.fail(new KiroPluginError(stderr.trim() || `kiro-cli chat exited${code === null ? "" : ` with code ${code}`}${signal ? ` and signal ${signal}` : ""}`, authError ? "KIRO_AUTH_ERROR" : "KIRO_CLI_FAILED", authError ? 401 : 502));
|
|
153
175
|
});
|
|
154
176
|
try {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type { AcpConnection, AcpNotificationHandler, AcpStdioClientOptions, Json
|
|
|
3
3
|
export { acpPermissionResponse, KiroAcpTransport } from "./acp-transport.js";
|
|
4
4
|
export type { AcpSessionClient, KiroAcpTransportOptions } from "./acp-transport.js";
|
|
5
5
|
export { createKiroPlugin, effectiveBackend, KiroPlugin } from "./plugin.js";
|
|
6
|
-
export { detectAuth, redacted, resolveApiKey } from "./auth.js";
|
|
6
|
+
export { detectAuth, extractKiroLoginUrl, KIRO_LOGIN_URL, redacted, resolveApiKey, startKiroCliLogin } from "./auth.js";
|
|
7
7
|
export { cliChatArgs, KiroCliChatTransport, promptForCli } from "./cli-transport.js";
|
|
8
8
|
export { createKiroFetch } from "./fetch-adapter.js";
|
|
9
9
|
export { loadOptions } from "./config.js";
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { KiroPlugin } from "./plugin.js";
|
|
|
2
2
|
export { AcpJsonRpcClient, createAcpStdioClient, decodeJsonRpc, encodeJsonRpc } from "./acp-client.js";
|
|
3
3
|
export { acpPermissionResponse, KiroAcpTransport } from "./acp-transport.js";
|
|
4
4
|
export { createKiroPlugin, effectiveBackend, KiroPlugin } from "./plugin.js";
|
|
5
|
-
export { detectAuth, redacted, resolveApiKey } from "./auth.js";
|
|
5
|
+
export { detectAuth, extractKiroLoginUrl, KIRO_LOGIN_URL, redacted, resolveApiKey, startKiroCliLogin } from "./auth.js";
|
|
6
6
|
export { cliChatArgs, KiroCliChatTransport, promptForCli } from "./cli-transport.js";
|
|
7
7
|
export { createKiroFetch } from "./fetch-adapter.js";
|
|
8
8
|
export { loadOptions } from "./config.js";
|
package/dist/plugin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin";
|
|
2
2
|
import { KiroAcpTransport } from "./acp-transport.js";
|
|
3
|
-
import { detectAuth, resolveApiKey } from "./auth.js";
|
|
3
|
+
import { detectAuth, resolveApiKey, startKiroCliLogin } from "./auth.js";
|
|
4
4
|
import { KiroCliChatTransport } from "./cli-transport.js";
|
|
5
5
|
import { loadOptions } from "./config.js";
|
|
6
6
|
import { createKiroFetch } from "./fetch-adapter.js";
|
|
@@ -168,6 +168,22 @@ export function createKiroPlugin() {
|
|
|
168
168
|
auth: {
|
|
169
169
|
provider: options.providerID,
|
|
170
170
|
methods: [
|
|
171
|
+
{
|
|
172
|
+
type: "oauth",
|
|
173
|
+
label: "Kiro CLI login",
|
|
174
|
+
authorize: async () => {
|
|
175
|
+
const session = startKiroCliLogin();
|
|
176
|
+
return {
|
|
177
|
+
url: session.url,
|
|
178
|
+
instructions: session.instructions,
|
|
179
|
+
method: "auto",
|
|
180
|
+
callback: async () => {
|
|
181
|
+
const authenticated = await session.waitForAuth();
|
|
182
|
+
return authenticated ? { type: "success", key: "kiro-plugin-local-transport" } : { type: "failed" };
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
},
|
|
171
187
|
{
|
|
172
188
|
type: "api",
|
|
173
189
|
label: "Kiro API key",
|