opencode-kiro 0.4.0 → 0.5.0-beta.2

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/dist/server.js CHANGED
@@ -1,258 +1,422 @@
1
- // src/server.ts
2
- import { readFileSync } from "fs";
3
- import { homedir } from "os";
4
- import { dirname, join } from "path";
5
- var KIRO_PLUGIN_NAME = "opencode-kiro";
6
- var server = async (input) => {
7
- const prompts = [
8
- {
9
- type: "select",
10
- key: "sidebar",
11
- message: "Enable the Kiro credits sidebar?",
12
- options: [
13
- { label: "Yes", value: "yes", hint: "writes tui.json; restart to apply" },
14
- { label: "No", value: "no" }
15
- ]
16
- }
17
- ];
1
+ // src/server/auth.ts
2
+ var KIRO_INTEGRATION_ID = "kiro";
3
+ var KIRO_INTEGRATION_NAME = "Kiro";
4
+ var KIRO_OAUTH_METHOD_ID = "kiro-cli-login";
5
+ var KIRO_OAUTH_METHOD_LABEL = "Kiro CLI Login";
6
+ var KIRO_DOCS_URL = "https://kiro.dev/docs/cli/";
7
+ var POLL_INTERVAL_MS = 2e3;
8
+ var MAX_WAIT_MS = 12e4;
9
+ var NOT_INSTALLED_MESSAGE = "kiro-cli is not installed. Install it from https://kiro.dev/docs/cli/";
10
+ var TIMEOUT_MESSAGE = "Kiro authentication timed out. Run `kiro-cli login` manually, then re-run `opencode auth login`.";
11
+ var LOGIN_INSTRUCTIONS = "Complete Kiro authentication in the browser window that just opened. Waiting for login...";
12
+ var ALREADY_AUTHENTICATED_INSTRUCTIONS = "Already authenticated with Kiro CLI.";
13
+ function createAuthResources() {
18
14
  return {
19
- auth: {
20
- provider: "kiro",
21
- // options forwarded into createKiroAcp(). maps each model's limit.context into contextWindows by api.id; zero/missing skipped (SDK falls back to 1M).
22
- loader: async (_getAuth, provider) => {
23
- void notifyIfTokenExpired(input.client);
24
- return {
25
- cwd: input.directory ?? input.worktree,
26
- agent: "opencode",
27
- trustAllTools: true,
28
- mcpTimeout: 45,
29
- contextWindows: Object.fromEntries(
30
- Object.values(provider?.models ?? {}).filter((m) => m.api?.id && (m.limit?.context ?? 0) > 0).map((m) => [m.api.id, m.limit.context])
31
- )
32
- };
33
- },
34
- methods: [
35
- {
36
- type: "oauth",
37
- label: "Kiro CLI Login",
38
- prompts,
39
- async authorize(inputs) {
40
- const { verifyAuth } = await import("kiro-acp-ai-provider");
41
- const status = verifyAuth();
42
- if (!status.installed)
43
- throw new Error(
44
- "kiro-cli is not installed. Install it from https://kiro.dev/docs/cli/"
45
- );
46
- if (inputs?.sidebar === "yes") await enableSidebarConfig(tuiConfigPath(), input);
47
- if (status.authenticated) {
48
- return {
49
- url: "",
50
- instructions: "",
51
- method: "auto",
52
- async callback() {
53
- return readToken(status.tokenPath);
54
- }
55
- };
56
- }
57
- const { execFile } = await import("child_process");
58
- const child = execFile("kiro-cli", ["login"], {
59
- shell: process.platform === "win32"
60
- });
61
- return {
62
- url: "",
63
- instructions: "Complete Kiro authentication in the browser window that just opened. Waiting for login...",
64
- method: "auto",
65
- async callback() {
66
- const maxWait = 12e4;
67
- const start = Date.now();
68
- while (Date.now() - start < maxWait) {
69
- await new Promise((r) => setTimeout(r, 2e3));
70
- const check = verifyAuth();
71
- if (check.authenticated) {
72
- child.kill();
73
- return readToken(check.tokenPath);
74
- }
75
- }
76
- child.kill();
77
- throw new Error(
78
- "Kiro authentication timed out. Run `kiro-cli login` manually."
79
- );
80
- }
81
- };
82
- }
83
- }
84
- ]
85
- },
86
- // crash guard: core derefs an undefined kiro provider during auth init when a cred exists but the catalog lacks kiro.
87
- // only inject when authed; ??= is idempotent and won't clobber a real catalog entry.
88
- config: async (input2) => {
89
- if (!hasStoredKiroCredential()) return;
90
- input2.provider ??= {};
91
- input2.provider.kiro ??= {};
92
- },
93
- provider: {
94
- id: "kiro",
95
- // expose the exact runtime/models.dev intersection with catalog metadata intact
96
- async models(provider, ctx) {
97
- if (!ctx.auth) return provider.models;
98
- try {
99
- const { listModels } = await import("kiro-acp-ai-provider");
100
- const discoveredModels = await listModels({
101
- cwd: input.directory ?? input.worktree
102
- });
103
- const runtimeModels = new Map(discoveredModels.map((model) => [model.modelId, model]));
104
- if (runtimeModels.size !== discoveredModels.length) return provider.models;
105
- const models = {};
106
- for (const [catalogKey, catalogModel] of Object.entries(provider.models)) {
107
- const apiId = catalogModel.api?.id;
108
- if (typeof apiId !== "string") continue;
109
- const runtimeModel = runtimeModels.get(apiId);
110
- if (!runtimeModel) continue;
111
- if (runtimeModel.runtimeEfforts.length === 0) {
112
- models[catalogKey] = catalogModel;
113
- continue;
114
- }
115
- models[catalogKey] = {
116
- ...catalogModel,
117
- ...runtimeModel.baselineEffort === void 0 ? {} : {
118
- options: {
119
- ...catalogModel.options,
120
- reasoningEffort: runtimeModel.baselineEffort
121
- }
122
- },
123
- variants: {
124
- ...catalogModel.variants,
125
- ...Object.fromEntries(
126
- runtimeModel.runtimeEfforts.map((effort) => [
127
- effort,
128
- {
129
- ...catalogModel.variants?.[effort],
130
- reasoningEffort: effort
131
- }
132
- ])
133
- )
134
- }
135
- };
136
- }
137
- return models;
138
- } catch {
139
- return provider.models;
140
- }
141
- }
142
- }
15
+ child: void 0,
16
+ pollTimer: void 0,
17
+ cancelPoll: void 0,
18
+ disposeRegistration: void 0
143
19
  };
144
- };
145
- function kiroTokenPath() {
146
- return join(homedir(), ".aws", "sso", "cache", "kiro-auth-token.json");
147
20
  }
148
- async function readKiroTokenFile(tokenPath) {
149
- try {
150
- const { readFile } = await import("fs/promises");
151
- const parsed = JSON.parse(await readFile(tokenPath, "utf8"));
152
- return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : void 0;
153
- } catch {
154
- return void 0;
21
+ function releaseLoginResources(resources) {
22
+ if (resources.pollTimer !== void 0) {
23
+ clearTimeout(resources.pollTimer);
24
+ resources.pollTimer = void 0;
25
+ }
26
+ if (resources.child !== void 0) {
27
+ resources.child.kill();
28
+ resources.child = void 0;
29
+ }
30
+ if (resources.cancelPoll !== void 0) {
31
+ const cancel = resources.cancelPoll;
32
+ resources.cancelPoll = void 0;
33
+ cancel();
155
34
  }
156
35
  }
157
- async function readToken(tokenPath) {
36
+ function kiroCredential() {
37
+ return {
38
+ type: "oauth",
39
+ methodID: KIRO_OAUTH_METHOD_ID,
40
+ refresh: "",
41
+ access: "kiro-cli",
42
+ expires: 0
43
+ };
44
+ }
45
+ function pollForLogin(verifyAuth, resources) {
46
+ return new Promise((resolve, reject) => {
47
+ const start = Date.now();
48
+ resources.cancelPoll = () => {
49
+ reject(new Error("Kiro authentication was cancelled."));
50
+ };
51
+ const tick = () => {
52
+ resources.pollTimer = void 0;
53
+ if (verifyAuth().authenticated) {
54
+ resources.cancelPoll = void 0;
55
+ releaseLoginResources(resources);
56
+ resolve(kiroCredential());
57
+ return;
58
+ }
59
+ if (Date.now() - start >= MAX_WAIT_MS) {
60
+ resources.cancelPoll = void 0;
61
+ releaseLoginResources(resources);
62
+ reject(new Error(TIMEOUT_MESSAGE));
63
+ return;
64
+ }
65
+ resources.pollTimer = setTimeout(tick, POLL_INTERVAL_MS);
66
+ };
67
+ resources.pollTimer = setTimeout(tick, POLL_INTERVAL_MS);
68
+ });
69
+ }
70
+ async function authorize(resources) {
158
71
  const { verifyAuth } = await import("kiro-acp-ai-provider");
159
- if (!verifyAuth().authenticated) return { type: "failed" };
160
- const token = tokenPath ? await readKiroTokenFile(tokenPath) : void 0;
161
- const access = typeof token?.accessToken === "string" ? token.accessToken : "";
162
- const refresh = typeof token?.refreshToken === "string" ? token.refreshToken : "";
72
+ const status = verifyAuth();
73
+ if (!status.installed) throw new Error(NOT_INSTALLED_MESSAGE);
74
+ if (status.authenticated) {
75
+ return {
76
+ url: KIRO_DOCS_URL,
77
+ instructions: ALREADY_AUTHENTICATED_INSTRUCTIONS,
78
+ mode: "auto",
79
+ callback: Promise.resolve(kiroCredential())
80
+ };
81
+ }
82
+ const { execFile } = await import("child_process");
83
+ resources.child = execFile("kiro-cli", ["login"], {
84
+ shell: process.platform === "win32"
85
+ });
163
86
  return {
164
- type: "success",
165
- refresh,
166
- // real refresh when present, else ""
167
- access: access || "authenticated",
168
- // cosmetic; opencode-core only needs presence
169
- // future expiry, refreshed each startup (server() re-runs per session) so core doesn't flag a logged-in user as expired. not the file value.
170
- expires: Date.now() + 8 * 60 * 60 * 1e3
87
+ url: KIRO_DOCS_URL,
88
+ instructions: LOGIN_INSTRUCTIONS,
89
+ mode: "auto",
90
+ callback: pollForLogin(verifyAuth, resources)
171
91
  };
172
92
  }
173
- async function notifyIfTokenExpired(client) {
174
- try {
175
- const { verifyAuth } = await import("kiro-acp-ai-provider");
176
- if (verifyAuth().authenticated) return;
177
- const message = "Kiro is not logged in. Run 'kiro-cli login' to authenticate.";
178
- console.warn(message);
179
- void client?.tui?.showToast?.({ body: { message, variant: "warning" } })?.catch(() => {
93
+ async function registerAuth(context, resources) {
94
+ const registration = await context.integration.transform((draft) => {
95
+ draft.update(KIRO_INTEGRATION_ID, (integration) => {
96
+ integration.name = KIRO_INTEGRATION_NAME;
180
97
  });
181
- } catch {
98
+ draft.method.update({
99
+ integrationID: KIRO_INTEGRATION_ID,
100
+ method: {
101
+ id: KIRO_OAUTH_METHOD_ID,
102
+ type: "oauth",
103
+ label: KIRO_OAUTH_METHOD_LABEL
104
+ },
105
+ authorize: async (_answer) => authorize(resources)
106
+ // no refresh callback: kiro-cli owns credential storage and refresh
107
+ });
108
+ });
109
+ resources.disposeRegistration = registration.dispose;
110
+ return async () => {
111
+ releaseLoginResources(resources);
112
+ resources.disposeRegistration = void 0;
113
+ await registration.dispose();
114
+ };
115
+ }
116
+
117
+ // src/server/discovery.ts
118
+ var KIRO_PROVIDER_ID = "kiro";
119
+ var KIRO_PROVIDER_PACKAGE = "aisdk:kiro-acp-ai-provider";
120
+ function asString(value) {
121
+ return value;
122
+ }
123
+ function createDiscoveryResources() {
124
+ return {
125
+ state: void 0,
126
+ disposeTransform: void 0,
127
+ eventIterator: void 0,
128
+ eventTask: void 0
129
+ };
130
+ }
131
+ function effortSettings(effort) {
132
+ return { effort };
133
+ }
134
+ function applyEfforts(model, runtimeModel) {
135
+ if (runtimeModel.runtimeEfforts.length === 0) return;
136
+ if (runtimeModel.baselineEffort !== void 0) {
137
+ model.settings = { ...model.settings, ...effortSettings(runtimeModel.baselineEffort) };
138
+ }
139
+ for (const effort of runtimeModel.runtimeEfforts) {
140
+ const existing = model.variants.find((variant) => asString(variant.id) === effort);
141
+ if (existing !== void 0) {
142
+ existing.settings = { ...existing.settings, ...effortSettings(effort) };
143
+ } else {
144
+ model.variants.push({
145
+ id: effort,
146
+ settings: effortSettings(effort)
147
+ });
148
+ }
182
149
  }
183
150
  }
184
- function hasStoredKiroCredential() {
185
- return authJsonPaths().some((path) => {
151
+ function applyCatalogSnapshot(draft, state) {
152
+ const snapshot = state.snapshot;
153
+ if (snapshot === void 0 || snapshot.length === 0) return;
154
+ const runtime = new Map(snapshot.map((model) => [model.modelId, model]));
155
+ const record = draft.provider.get(KIRO_PROVIDER_ID);
156
+ const rich = record !== void 0 && record.models.size > 0;
157
+ const contextWindows = {};
158
+ if (rich) {
159
+ for (const [catalogKey, catalogModel] of Array.from(record.models.entries())) {
160
+ const runtimeModel = runtime.get(asString(catalogModel.modelID));
161
+ if (runtimeModel === void 0) {
162
+ draft.model.remove(KIRO_PROVIDER_ID, catalogKey);
163
+ continue;
164
+ }
165
+ draft.model.update(KIRO_PROVIDER_ID, catalogKey, (model) => {
166
+ applyEfforts(model, runtimeModel);
167
+ if (model.limit.context > 0) contextWindows[asString(model.modelID)] = model.limit.context;
168
+ });
169
+ }
170
+ } else {
171
+ for (const runtimeModel of snapshot) {
172
+ draft.model.update(KIRO_PROVIDER_ID, runtimeModel.modelId, (model) => {
173
+ model.name = runtimeModel.name || runtimeModel.modelId;
174
+ model.modelID = runtimeModel.modelId;
175
+ applyEfforts(model, runtimeModel);
176
+ if (model.limit.context > 0) contextWindows[asString(model.modelID)] = model.limit.context;
177
+ });
178
+ }
179
+ }
180
+ draft.provider.update(KIRO_PROVIDER_ID, (provider) => {
181
+ provider.name = KIRO_INTEGRATION_NAME;
182
+ provider.integrationID = KIRO_INTEGRATION_ID;
183
+ provider.package = KIRO_PROVIDER_PACKAGE;
184
+ provider.settings = {
185
+ ...provider.settings,
186
+ cwd: state.cwd,
187
+ agent: "opencode",
188
+ trustAllTools: true,
189
+ mcpTimeout: 45,
190
+ contextWindows
191
+ };
192
+ });
193
+ }
194
+ function createDiscover(context, state) {
195
+ async function runDiscovery(gen) {
186
196
  try {
187
- const parsed = JSON.parse(readFileSync(path, "utf8"));
188
- return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && "kiro" in parsed;
197
+ const { listModels } = await import("kiro-acp-ai-provider");
198
+ const discovered = await listModels({ cwd: state.cwd });
199
+ if (gen !== state.generation) return;
200
+ const uniqueIds = new Set(discovered.map((model) => model.modelId));
201
+ if (uniqueIds.size !== discovered.length) return;
202
+ state.snapshot = discovered;
203
+ await context.catalog.reload();
189
204
  } catch {
190
- return false;
191
205
  }
192
- });
206
+ }
207
+ return async function discover(_reason) {
208
+ const active = await context.integration.connection.active(KIRO_INTEGRATION_ID);
209
+ if (!active) {
210
+ state.generation += 1;
211
+ state.snapshot = void 0;
212
+ await context.catalog.reload();
213
+ return;
214
+ }
215
+ if (state.inflight !== void 0) return state.inflight;
216
+ const gen = ++state.generation;
217
+ const run = runDiscovery(gen).finally(() => {
218
+ if (state.inflight === run) state.inflight = void 0;
219
+ });
220
+ state.inflight = run;
221
+ return run;
222
+ };
223
+ }
224
+ async function consumeEvents(iterator, discover) {
225
+ try {
226
+ while (true) {
227
+ const result = await iterator.next();
228
+ if (result.done) return;
229
+ const event = result.value;
230
+ if (event.type === "integration.connection.updated" && event.data.integrationID === KIRO_INTEGRATION_ID) {
231
+ try {
232
+ await discover("connection-updated");
233
+ } catch {
234
+ }
235
+ }
236
+ }
237
+ } catch {
238
+ }
193
239
  }
194
- function authJsonPaths() {
195
- if (process.env.XDG_DATA_HOME) return [join(process.env.XDG_DATA_HOME, "opencode", "auth.json")];
196
- const current = join(homedir(), ".local", "share", "opencode", "auth.json");
197
- if (process.platform !== "win32") return [current];
198
- return [current, join(homedir(), ".opencode", "auth.json")];
240
+ async function registerDiscovery(context, resources) {
241
+ const { location } = await context.integration.list();
242
+ const state = {
243
+ cwd: location.directory,
244
+ snapshot: void 0,
245
+ generation: 0,
246
+ inflight: void 0
247
+ };
248
+ resources.state = state;
249
+ const discover = createDiscover(context, state);
250
+ const registration = await context.catalog.transform(
251
+ (draft) => applyCatalogSnapshot(draft, state)
252
+ );
253
+ resources.disposeTransform = registration.dispose;
254
+ const iterator = context.event.subscribe()[Symbol.asyncIterator]();
255
+ resources.eventIterator = iterator;
256
+ resources.eventTask = consumeEvents(iterator, discover);
257
+ if (await context.integration.connection.active(KIRO_INTEGRATION_ID)) {
258
+ void discover("setup").catch(() => {
259
+ });
260
+ }
261
+ return async () => {
262
+ state.generation += 1;
263
+ const errors = [];
264
+ const eventIterator = resources.eventIterator;
265
+ resources.eventIterator = void 0;
266
+ if (eventIterator?.return !== void 0) {
267
+ try {
268
+ await eventIterator.return();
269
+ } catch (error) {
270
+ errors.push(error);
271
+ }
272
+ }
273
+ const eventTask = resources.eventTask;
274
+ resources.eventTask = void 0;
275
+ if (eventTask !== void 0) await eventTask;
276
+ const disposeTransform = resources.disposeTransform;
277
+ resources.disposeTransform = void 0;
278
+ if (disposeTransform !== void 0) {
279
+ try {
280
+ await disposeTransform();
281
+ } catch (error) {
282
+ errors.push(error);
283
+ }
284
+ }
285
+ if (errors.length > 0) throw new AggregateError(errors, "kiro discovery cleanup failures");
286
+ };
199
287
  }
200
- function tuiConfigPath() {
201
- const base = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
202
- return join(base, "opencode", "tui.json");
288
+
289
+ // src/server/aisdk.ts
290
+ var KIRO_SDK_PACKAGE = "kiro-acp-ai-provider";
291
+ function createAisdkResources() {
292
+ return {
293
+ ownedSdks: /* @__PURE__ */ new Set(),
294
+ cache: /* @__PURE__ */ new Map(),
295
+ disposeHook: void 0
296
+ };
203
297
  }
204
- function isSidebarConfigured(config) {
205
- if (!config) return false;
206
- const plugin = config.plugin;
207
- return Array.isArray(plugin) && plugin.includes(KIRO_PLUGIN_NAME);
298
+ function isKiroPackage(pkg) {
299
+ return pkg === KIRO_SDK_PACKAGE || pkg === `aisdk:${KIRO_SDK_PACKAGE}`;
208
300
  }
209
- async function enableSidebarConfig(path, input) {
210
- try {
211
- const { readFile, writeFile, mkdir } = await import("fs/promises");
212
- let raw;
301
+ function isJsonSafe(value) {
302
+ if (value === null) return true;
303
+ const kind = typeof value;
304
+ if (kind === "string" || kind === "number" || kind === "boolean") return true;
305
+ if (Array.isArray(value)) return value.every(isJsonSafe);
306
+ if (kind === "object") {
307
+ const proto = Object.getPrototypeOf(value);
308
+ if (proto !== Object.prototype && proto !== null) return false;
309
+ return Object.values(value).every(isJsonSafe);
310
+ }
311
+ return false;
312
+ }
313
+ function stableStringify(value) {
314
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
315
+ if (typeof value === "object" && value !== null) {
316
+ const entries = Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`);
317
+ return `{${entries.join(",")}}`;
318
+ }
319
+ return JSON.stringify(value);
320
+ }
321
+ function stableOptionsKey(options) {
322
+ return isJsonSafe(options) ? stableStringify(options) : void 0;
323
+ }
324
+ async function registerAisdkHook(context, resources) {
325
+ const registration = await context.aisdk.hook(
326
+ "sdk",
327
+ async (event) => {
328
+ if (!isKiroPackage(event.package)) return;
329
+ const { createKiroAcp } = await import("kiro-acp-ai-provider");
330
+ const key = stableOptionsKey(event.options);
331
+ let sdk = key === void 0 ? void 0 : resources.cache.get(key);
332
+ if (sdk === void 0) {
333
+ sdk = createKiroAcp(event.options);
334
+ resources.ownedSdks.add(sdk);
335
+ if (key !== void 0) resources.cache.set(key, sdk);
336
+ }
337
+ event.sdk = sdk;
338
+ },
339
+ { providerID: KIRO_PROVIDER_ID }
340
+ );
341
+ resources.disposeHook = registration.dispose;
342
+ return async () => {
343
+ const errors = [];
344
+ resources.disposeHook = void 0;
213
345
  try {
214
- raw = await readFile(path, "utf8");
215
- } catch {
216
- raw = void 0;
346
+ await registration.dispose();
347
+ } catch (error) {
348
+ errors.push(error);
217
349
  }
218
- let config;
219
- if (raw === void 0) {
220
- config = {};
221
- } else {
350
+ const owned = Array.from(resources.ownedSdks);
351
+ resources.ownedSdks.clear();
352
+ resources.cache.clear();
353
+ for (const sdk of owned) {
222
354
  try {
223
- const parsed = JSON.parse(raw);
224
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return;
225
- config = parsed;
226
- } catch {
227
- return;
355
+ await sdk.shutdown();
356
+ } catch (error) {
357
+ errors.push(error);
228
358
  }
229
359
  }
230
- if (isSidebarConfigured(config)) return;
231
- const plugin = Array.isArray(config.plugin) ? [...config.plugin] : [];
232
- if (!plugin.includes(KIRO_PLUGIN_NAME)) plugin.push(KIRO_PLUGIN_NAME);
233
- config.plugin = plugin;
234
- await mkdir(dirname(path), { recursive: true });
235
- await writeFile(path, JSON.stringify(config, null, 2) + "\n", "utf8");
360
+ if (errors.length > 0) throw new AggregateError(errors, "kiro aisdk cleanup failures");
361
+ };
362
+ }
363
+
364
+ // src/server/lifecycle.ts
365
+ function createServerState() {
366
+ return {
367
+ auth: createAuthResources(),
368
+ discovery: createDiscoveryResources(),
369
+ aisdk: createAisdkResources(),
370
+ disposers: [],
371
+ disposed: false,
372
+ cleanup: void 0
373
+ };
374
+ }
375
+ async function disposeAll(disposers) {
376
+ const errors = [];
377
+ for (const dispose of [...disposers].reverse()) {
378
+ try {
379
+ await dispose();
380
+ } catch (error) {
381
+ if (error instanceof AggregateError) errors.push(...error.errors);
382
+ else errors.push(error);
383
+ }
384
+ }
385
+ if (errors.length > 0) throw new AggregateError(errors, "kiro server cleanup failures");
386
+ }
387
+ function buildCleanup(state) {
388
+ return () => {
389
+ if (state.cleanup !== void 0) return state.cleanup;
390
+ state.disposed = true;
391
+ state.cleanup = disposeAll(state.disposers);
392
+ return state.cleanup;
393
+ };
394
+ }
395
+
396
+ // src/server.ts
397
+ var plugin = {
398
+ id: "kiro",
399
+ // tui: true (dist/promise/plugin.d.ts:40) — the host auto-loads this
400
+ // package's `./tui` entrypoint for npm-channel installs (single config entry)
401
+ tui: true,
402
+ async setup(context) {
403
+ const state = createServerState();
404
+ const cleanup = buildCleanup(state);
236
405
  try {
237
- await input.client.tui.showToast({
238
- body: {
239
- message: "Kiro credits sidebar enabled. Restart opencode to see it.",
240
- variant: "success"
241
- }
406
+ state.disposers.push(await registerAuth(context, state.auth));
407
+ state.disposers.push(await registerDiscovery(context, state.discovery));
408
+ state.disposers.push(await registerAisdkHook(context, state.aisdk));
409
+ } catch (error) {
410
+ await Promise.resolve(cleanup()).catch(() => {
242
411
  });
243
- } catch {
412
+ throw error;
244
413
  }
245
- } catch (err) {
246
- console.error("opencode-kiro: failed to enable the Kiro credits sidebar in tui.json:", err);
414
+ return cleanup;
247
415
  }
248
- }
249
- var KiroAuthPlugin = server;
250
- var server_default = { id: "kiro", server };
416
+ };
417
+ var server_default = plugin;
418
+ var KiroAuthPlugin = plugin;
251
419
  export {
252
420
  KiroAuthPlugin,
253
- server_default as default,
254
- enableSidebarConfig,
255
- kiroTokenPath,
256
- notifyIfTokenExpired,
257
- readToken
421
+ server_default as default
258
422
  };