openrouter-genimage-kote-opencode 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.
Files changed (163) hide show
  1. package/.opencode/skills/openrouter-image-generation/SKILL.md +379 -0
  2. package/CHANGELOG.md +10 -0
  3. package/LICENSE +21 -0
  4. package/README.md +229 -0
  5. package/README.ru.md +229 -0
  6. package/SECURITY.md +31 -0
  7. package/THIRD_PARTY_NOTICES.md +16 -0
  8. package/dist/cli.d.ts +20 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +277 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/commands/balance-command.d.ts +5 -0
  13. package/dist/commands/balance-command.d.ts.map +1 -0
  14. package/dist/commands/balance-command.js +20 -0
  15. package/dist/commands/balance-command.js.map +1 -0
  16. package/dist/config/resolve.d.ts +5 -0
  17. package/dist/config/resolve.d.ts.map +1 -0
  18. package/dist/config/resolve.js +113 -0
  19. package/dist/config/resolve.js.map +1 -0
  20. package/dist/config/schema.d.ts +188 -0
  21. package/dist/config/schema.d.ts.map +1 -0
  22. package/dist/config/schema.js +144 -0
  23. package/dist/config/schema.js.map +1 -0
  24. package/dist/config/types.d.ts +75 -0
  25. package/dist/config/types.d.ts.map +1 -0
  26. package/dist/config/types.js +30 -0
  27. package/dist/config/types.js.map +1 -0
  28. package/dist/logging.d.ts +46 -0
  29. package/dist/logging.d.ts.map +1 -0
  30. package/dist/logging.js +58 -0
  31. package/dist/logging.js.map +1 -0
  32. package/dist/openrouter/capabilities.d.ts +22 -0
  33. package/dist/openrouter/capabilities.d.ts.map +1 -0
  34. package/dist/openrouter/capabilities.js +137 -0
  35. package/dist/openrouter/capabilities.js.map +1 -0
  36. package/dist/openrouter/client.d.ts +54 -0
  37. package/dist/openrouter/client.d.ts.map +1 -0
  38. package/dist/openrouter/client.js +440 -0
  39. package/dist/openrouter/client.js.map +1 -0
  40. package/dist/openrouter/errors.d.ts +22 -0
  41. package/dist/openrouter/errors.d.ts.map +1 -0
  42. package/dist/openrouter/errors.js +75 -0
  43. package/dist/openrouter/errors.js.map +1 -0
  44. package/dist/openrouter/models.d.ts +37 -0
  45. package/dist/openrouter/models.d.ts.map +1 -0
  46. package/dist/openrouter/models.js +113 -0
  47. package/dist/openrouter/models.js.map +1 -0
  48. package/dist/openrouter/requests.d.ts +6 -0
  49. package/dist/openrouter/requests.d.ts.map +1 -0
  50. package/dist/openrouter/requests.js +74 -0
  51. package/dist/openrouter/requests.js.map +1 -0
  52. package/dist/openrouter/schemas.d.ts +37 -0
  53. package/dist/openrouter/schemas.d.ts.map +1 -0
  54. package/dist/openrouter/schemas.js +2 -0
  55. package/dist/openrouter/schemas.js.map +1 -0
  56. package/dist/output/attachments.d.ts +10 -0
  57. package/dist/output/attachments.d.ts.map +1 -0
  58. package/dist/output/attachments.js +13 -0
  59. package/dist/output/attachments.js.map +1 -0
  60. package/dist/output/formats.d.ts +42 -0
  61. package/dist/output/formats.d.ts.map +1 -0
  62. package/dist/output/formats.js +126 -0
  63. package/dist/output/formats.js.map +1 -0
  64. package/dist/output/index.d.ts +5 -0
  65. package/dist/output/index.d.ts.map +1 -0
  66. package/dist/output/index.js +5 -0
  67. package/dist/output/index.js.map +1 -0
  68. package/dist/output/paths.d.ts +38 -0
  69. package/dist/output/paths.d.ts.map +1 -0
  70. package/dist/output/paths.js +296 -0
  71. package/dist/output/paths.js.map +1 -0
  72. package/dist/output/writer.d.ts +33 -0
  73. package/dist/output/writer.d.ts.map +1 -0
  74. package/dist/output/writer.js +176 -0
  75. package/dist/output/writer.js.map +1 -0
  76. package/dist/references/attachments.d.ts +82 -0
  77. package/dist/references/attachments.d.ts.map +1 -0
  78. package/dist/references/attachments.js +238 -0
  79. package/dist/references/attachments.js.map +1 -0
  80. package/dist/references/data-url.d.ts +12 -0
  81. package/dist/references/data-url.d.ts.map +1 -0
  82. package/dist/references/data-url.js +75 -0
  83. package/dist/references/data-url.js.map +1 -0
  84. package/dist/references/formats.d.ts +13 -0
  85. package/dist/references/formats.d.ts.map +1 -0
  86. package/dist/references/formats.js +50 -0
  87. package/dist/references/formats.js.map +1 -0
  88. package/dist/references/index.d.ts +7 -0
  89. package/dist/references/index.d.ts.map +1 -0
  90. package/dist/references/index.js +7 -0
  91. package/dist/references/index.js.map +1 -0
  92. package/dist/references/limits.d.ts +15 -0
  93. package/dist/references/limits.d.ts.map +1 -0
  94. package/dist/references/limits.js +62 -0
  95. package/dist/references/limits.js.map +1 -0
  96. package/dist/references/project-files.d.ts +13 -0
  97. package/dist/references/project-files.d.ts.map +1 -0
  98. package/dist/references/project-files.js +148 -0
  99. package/dist/references/project-files.js.map +1 -0
  100. package/dist/references/remote-urls.d.ts +3 -0
  101. package/dist/references/remote-urls.d.ts.map +1 -0
  102. package/dist/references/remote-urls.js +22 -0
  103. package/dist/references/remote-urls.js.map +1 -0
  104. package/dist/server.d.ts +8 -0
  105. package/dist/server.d.ts.map +1 -0
  106. package/dist/server.js +97 -0
  107. package/dist/server.js.map +1 -0
  108. package/dist/tools/balance.d.ts +8 -0
  109. package/dist/tools/balance.d.ts.map +1 -0
  110. package/dist/tools/balance.js +133 -0
  111. package/dist/tools/balance.js.map +1 -0
  112. package/dist/tools/common.d.ts +3 -0
  113. package/dist/tools/common.d.ts.map +1 -0
  114. package/dist/tools/common.js +18 -0
  115. package/dist/tools/common.js.map +1 -0
  116. package/dist/tools/edit-image.d.ts +90 -0
  117. package/dist/tools/edit-image.d.ts.map +1 -0
  118. package/dist/tools/edit-image.js +47 -0
  119. package/dist/tools/edit-image.js.map +1 -0
  120. package/dist/tools/generate-image.d.ts +70 -0
  121. package/dist/tools/generate-image.d.ts.map +1 -0
  122. package/dist/tools/generate-image.js +36 -0
  123. package/dist/tools/generate-image.js.map +1 -0
  124. package/dist/tools/generation.d.ts +19 -0
  125. package/dist/tools/generation.d.ts.map +1 -0
  126. package/dist/tools/generation.js +154 -0
  127. package/dist/tools/generation.js.map +1 -0
  128. package/dist/tools/schema.d.ts +127 -0
  129. package/dist/tools/schema.d.ts.map +1 -0
  130. package/dist/tools/schema.js +47 -0
  131. package/dist/tools/schema.js.map +1 -0
  132. package/dist/tools/types.d.ts +18 -0
  133. package/dist/tools/types.d.ts.map +1 -0
  134. package/dist/tools/types.js +2 -0
  135. package/dist/tools/types.js.map +1 -0
  136. package/dist/transport/direct.d.ts +10 -0
  137. package/dist/transport/direct.d.ts.map +1 -0
  138. package/dist/transport/direct.js +15 -0
  139. package/dist/transport/direct.js.map +1 -0
  140. package/dist/transport/index.d.ts +4 -0
  141. package/dist/transport/index.d.ts.map +1 -0
  142. package/dist/transport/index.js +4 -0
  143. package/dist/transport/index.js.map +1 -0
  144. package/dist/transport/kote-gateway.d.ts +19 -0
  145. package/dist/transport/kote-gateway.d.ts.map +1 -0
  146. package/dist/transport/kote-gateway.js +224 -0
  147. package/dist/transport/kote-gateway.js.map +1 -0
  148. package/dist/transport/types.d.ts +10 -0
  149. package/dist/transport/types.d.ts.map +1 -0
  150. package/dist/transport/types.js +9 -0
  151. package/dist/transport/types.js.map +1 -0
  152. package/dist/utils/abort.d.ts +19 -0
  153. package/dist/utils/abort.d.ts.map +1 -0
  154. package/dist/utils/abort.js +121 -0
  155. package/dist/utils/abort.js.map +1 -0
  156. package/docs/architecture.md +75 -0
  157. package/docs/configuration.md +110 -0
  158. package/docs/image-to-image.md +101 -0
  159. package/docs/security.md +49 -0
  160. package/docs/testing.md +81 -0
  161. package/docs/troubleshooting.md +73 -0
  162. package/package.json +88 -0
  163. package/schema/plugin-options.schema.json +182 -0
@@ -0,0 +1,19 @@
1
+ import { type KoteGatewayClient } from "@koteye/kote-gateway-opencode/core";
2
+ import { type SafeLogger } from "../logging.js";
3
+ import { type FetchLike } from "./direct.js";
4
+ import { type HttpTransport } from "./types.js";
5
+ type GatewayClientFactory = () => Promise<KoteGatewayClient>;
6
+ export interface KoteGatewayTransportOptions {
7
+ logger: SafeLogger;
8
+ gatewayClientFactory?: GatewayClientFactory;
9
+ bunFetch?: FetchLike;
10
+ forceRuntime?: "bun" | "node";
11
+ }
12
+ export declare class KoteGatewayTransport implements HttpTransport {
13
+ #private;
14
+ constructor(options: KoteGatewayTransportOptions);
15
+ request(url: URL, init: RequestInit, signal: AbortSignal): Promise<Response>;
16
+ dispose(): Promise<void>;
17
+ }
18
+ export {};
19
+ //# sourceMappingURL=kote-gateway.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kote-gateway.d.ts","sourceRoot":"","sources":["../../src/transport/kote-gateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,oCAAoC,CAAA;AAO3C,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAO9D,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,YAAY,CAAA;AAEnB,KAAK,oBAAoB,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAG5D,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,UAAU,CAAA;IAClB,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,YAAY,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;CAC9B;AAsJD,qBAAa,oBAAqB,YAAW,aAAa;;gBAU5C,OAAO,EAAE,2BAA2B;IAW1C,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAuD5E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAgD/B"}
@@ -0,0 +1,224 @@
1
+ import { createKoteGatewayClient, KoteGatewayError, } from "@koteye/kote-gateway-opencode/core";
2
+ import { fetch as undiciFetch, ProxyAgent, } from "undici";
3
+ import { createHash } from "node:crypto";
4
+ import { safeUrlOrigin } from "../logging.js";
5
+ import { abortablePromise, createLinkedAbortSignal, isAbortError, throwIfAborted, } from "../utils/abort.js";
6
+ import { nativeFetch } from "./direct.js";
7
+ import { GatewayTransportError, } from "./types.js";
8
+ const MAX_CACHED_DISPATCHERS = 8;
9
+ function defaultGatewayClientFactory(disposeSignal) {
10
+ const fetchUntilDisposed = async (input, init = {}) => {
11
+ const signals = [disposeSignal];
12
+ if (init.signal !== undefined && init.signal !== null) {
13
+ signals.push(init.signal);
14
+ }
15
+ const linked = createLinkedAbortSignal(signals);
16
+ try {
17
+ return await nativeFetch(input, { ...init, signal: linked.signal });
18
+ }
19
+ finally {
20
+ linked.dispose();
21
+ }
22
+ };
23
+ return createKoteGatewayClient({
24
+ fetch: fetchUntilDisposed,
25
+ config: {
26
+ version: 1,
27
+ default: "proxy",
28
+ strict: true,
29
+ providers: { openrouter: "proxy" },
30
+ },
31
+ });
32
+ }
33
+ function mapGatewayError(error) {
34
+ if (error instanceof GatewayTransportError)
35
+ return error;
36
+ let code = "GENIMAGE_GATEWAY_CONNECT_FAILED";
37
+ if (error instanceof KoteGatewayError) {
38
+ switch (error.code) {
39
+ case "KOTE_BOOTSTRAP_SIGNATURE_INVALID":
40
+ code = "GENIMAGE_GATEWAY_SIGNATURE_INVALID";
41
+ break;
42
+ case "KOTE_BOOTSTRAP_EXPIRED":
43
+ code = "GENIMAGE_GATEWAY_CACHE_EXPIRED";
44
+ break;
45
+ case "KOTE_BOOTSTRAP_UNAVAILABLE":
46
+ case "KOTE_BOOTSTRAP_INVALID":
47
+ case "KOTE_CONFIG_INVALID":
48
+ case "KOTE_CONFIG_NOT_FOUND":
49
+ code = "GENIMAGE_GATEWAY_BOOTSTRAP_FAILED";
50
+ break;
51
+ case "KOTE_GATEWAY_UNAVAILABLE":
52
+ case "KOTE_PROXY_REQUEST_FAILED":
53
+ case "KOTE_UNSUPPORTED_TRANSPORT":
54
+ default:
55
+ code = "GENIMAGE_GATEWAY_CONNECT_FAILED";
56
+ }
57
+ }
58
+ return new GatewayTransportError(code, "KoteGateway could not complete the proxied OpenRouter request. Direct fallback is disabled.");
59
+ }
60
+ function validateDescriptor(descriptor) {
61
+ let url;
62
+ try {
63
+ url = new URL(descriptor.proxyUrl);
64
+ }
65
+ catch (error) {
66
+ throw new GatewayTransportError("GENIMAGE_GATEWAY_BOOTSTRAP_FAILED", "KoteGateway returned an invalid proxy descriptor.", { cause: error });
67
+ }
68
+ if ((url.protocol !== "https:" && url.protocol !== "http:") ||
69
+ url.username !== "" ||
70
+ url.password !== "" ||
71
+ descriptor.proxyUrl.length > 4_096) {
72
+ throw new GatewayTransportError("GENIMAGE_GATEWAY_BOOTSTRAP_FAILED", "KoteGateway returned an unsafe proxy descriptor.");
73
+ }
74
+ const proxyHeaders = descriptor.proxyHeaders;
75
+ if (proxyHeaders !== undefined &&
76
+ (typeof proxyHeaders !== "object" ||
77
+ proxyHeaders === null ||
78
+ Array.isArray(proxyHeaders))) {
79
+ throw new GatewayTransportError("GENIMAGE_GATEWAY_BOOTSTRAP_FAILED", "KoteGateway returned invalid proxy headers.");
80
+ }
81
+ for (const [name, value] of Object.entries(proxyHeaders ?? {})) {
82
+ if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,256}$/u.test(name) ||
83
+ typeof value !== "string" ||
84
+ value.length > 4_096 ||
85
+ /[\u0000-\u001f\u007f]/u.test(value)) {
86
+ throw new GatewayTransportError("GENIMAGE_GATEWAY_BOOTSTRAP_FAILED", "KoteGateway returned invalid proxy headers.");
87
+ }
88
+ }
89
+ return descriptor;
90
+ }
91
+ function splitProxyHeaders(headers) {
92
+ if (headers === undefined)
93
+ return {};
94
+ let token;
95
+ const remaining = {};
96
+ for (const [name, value] of Object.entries(headers)) {
97
+ if (name.toLowerCase() === "proxy-authorization")
98
+ token = value;
99
+ else
100
+ remaining[name] = value;
101
+ }
102
+ return {
103
+ ...(Object.keys(remaining).length === 0 ? {} : { headers: remaining }),
104
+ ...(token === undefined ? {} : { token }),
105
+ };
106
+ }
107
+ function descriptorKey(descriptor) {
108
+ const headers = Object.entries(descriptor.proxyHeaders ?? {}).sort(([left], [right]) => left.localeCompare(right));
109
+ return createHash("sha256")
110
+ .update(JSON.stringify([descriptor.proxyUrl, headers]))
111
+ .digest("hex");
112
+ }
113
+ export class KoteGatewayTransport {
114
+ #logger;
115
+ #gatewayClientFactory;
116
+ #bunFetch;
117
+ #runtime;
118
+ #dispatchers = new Map();
119
+ #disposeController = new AbortController();
120
+ #clientPromise;
121
+ #disposed = false;
122
+ constructor(options) {
123
+ this.#logger = options.logger;
124
+ this.#gatewayClientFactory =
125
+ options.gatewayClientFactory ??
126
+ (() => defaultGatewayClientFactory(this.#disposeController.signal));
127
+ this.#bunFetch = options.bunFetch ?? nativeFetch;
128
+ this.#runtime =
129
+ options.forceRuntime ??
130
+ (typeof process.versions.bun === "string" ? "bun" : "node");
131
+ }
132
+ async request(url, init, signal) {
133
+ if (this.#disposed) {
134
+ throw new GatewayTransportError("GENIMAGE_GATEWAY_CONNECT_FAILED", "KoteGateway transport is already disposed.");
135
+ }
136
+ const linked = createLinkedAbortSignal([
137
+ signal,
138
+ this.#disposeController.signal,
139
+ ]);
140
+ const requestSignal = linked.signal;
141
+ try {
142
+ const client = await abortablePromise(this.#getClient(), requestSignal);
143
+ const descriptor = validateDescriptor(await abortablePromise(client.getGateway(), requestSignal));
144
+ throwIfAborted(requestSignal);
145
+ const gatewayOrigin = safeUrlOrigin(descriptor.proxyUrl);
146
+ this.#logger.log("debug", "Routing OpenRouter request through KoteGateway.", {
147
+ routing: "proxy",
148
+ ...(gatewayOrigin === undefined ? {} : { gatewayOrigin }),
149
+ });
150
+ if (this.#runtime === "bun") {
151
+ const proxy = descriptor.proxyHeaders === undefined
152
+ ? descriptor.proxyUrl
153
+ : {
154
+ url: descriptor.proxyUrl,
155
+ headers: { ...descriptor.proxyHeaders },
156
+ };
157
+ return await this.#bunFetch(url, {
158
+ ...init,
159
+ signal: requestSignal,
160
+ proxy,
161
+ });
162
+ }
163
+ const dispatcher = this.#getDispatcher(descriptor);
164
+ return (await undiciFetch(url, {
165
+ ...init,
166
+ signal: requestSignal,
167
+ dispatcher,
168
+ }));
169
+ }
170
+ catch (error) {
171
+ if (requestSignal.aborted || isAbortError(error))
172
+ throw error;
173
+ this.#clientPromise?.then((client) => client.invalidateGateway()).catch(() => { });
174
+ throw mapGatewayError(error);
175
+ }
176
+ finally {
177
+ linked.dispose();
178
+ }
179
+ }
180
+ async dispose() {
181
+ this.#disposed = true;
182
+ this.#disposeController.abort(new DOMException("KoteGateway transport was disposed", "AbortError"));
183
+ this.#clientPromise = undefined;
184
+ const dispatchers = [...this.#dispatchers.values()];
185
+ this.#dispatchers.clear();
186
+ await Promise.all(dispatchers.map(async (dispatcher) => dispatcher.close().catch(() => { })));
187
+ }
188
+ #getClient() {
189
+ if (this.#clientPromise !== undefined)
190
+ return this.#clientPromise;
191
+ const created = Promise.resolve().then(() => this.#gatewayClientFactory());
192
+ const guarded = created.catch((error) => {
193
+ if (this.#clientPromise === guarded)
194
+ this.#clientPromise = undefined;
195
+ throw error;
196
+ });
197
+ this.#clientPromise = guarded;
198
+ return this.#clientPromise;
199
+ }
200
+ #getDispatcher(descriptor) {
201
+ const key = descriptorKey(descriptor);
202
+ const existing = this.#dispatchers.get(key);
203
+ if (existing !== undefined) {
204
+ this.#dispatchers.delete(key);
205
+ this.#dispatchers.set(key, existing);
206
+ return existing;
207
+ }
208
+ if (this.#dispatchers.size >= MAX_CACHED_DISPATCHERS) {
209
+ const oldest = this.#dispatchers.entries().next();
210
+ if (!oldest.done) {
211
+ this.#dispatchers.delete(oldest.value[0]);
212
+ void oldest.value[1].close().catch(() => undefined);
213
+ }
214
+ }
215
+ const proxyHeaders = splitProxyHeaders(descriptor.proxyHeaders);
216
+ const dispatcher = new ProxyAgent({
217
+ uri: descriptor.proxyUrl,
218
+ ...proxyHeaders,
219
+ });
220
+ this.#dispatchers.set(key, dispatcher);
221
+ return dispatcher;
222
+ }
223
+ }
224
+ //# sourceMappingURL=kote-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kote-gateway.js","sourceRoot":"","sources":["../../src/transport/kote-gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface HttpTransport {
2
+ request(url: URL, init: RequestInit, signal: AbortSignal): Promise<Response>;
3
+ dispose?(): Promise<void>;
4
+ }
5
+ export type GatewayErrorCode = "GENIMAGE_GATEWAY_BOOTSTRAP_FAILED" | "GENIMAGE_GATEWAY_SIGNATURE_INVALID" | "GENIMAGE_GATEWAY_CACHE_EXPIRED" | "GENIMAGE_GATEWAY_CONNECT_FAILED" | "GENIMAGE_GATEWAY_TIMEOUT";
6
+ export declare class GatewayTransportError extends Error {
7
+ readonly code: GatewayErrorCode;
8
+ constructor(code: GatewayErrorCode, message: string, options?: ErrorOptions);
9
+ }
10
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/transport/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC5E,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED,MAAM,MAAM,gBAAgB,GACxB,mCAAmC,GACnC,oCAAoC,GACpC,gCAAgC,GAChC,iCAAiC,GACjC,0BAA0B,CAAA;AAE9B,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;gBAEnB,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAK5E"}
@@ -0,0 +1,9 @@
1
+ export class GatewayTransportError extends Error {
2
+ code;
3
+ constructor(code, message, options) {
4
+ super(message, options);
5
+ this.name = "GatewayTransportError";
6
+ this.code = code;
7
+ }
8
+ }
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/transport/types.ts"],"names":[],"mappings":"AAYA,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IACrC,IAAI,CAAkB;IAE/B,YAAY,IAAsB,EAAE,OAAe,EAAE,OAAsB;QACzE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACvB,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAA;QACnC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ export interface DeadlineSignal {
2
+ signal: AbortSignal;
3
+ didTimeout(): boolean;
4
+ dispose(): void;
5
+ }
6
+ export interface LinkedAbortSignal {
7
+ signal: AbortSignal;
8
+ dispose(): void;
9
+ }
10
+ export declare function createLinkedAbortSignal(signals: readonly AbortSignal[]): LinkedAbortSignal;
11
+ export declare function createDeadlineSignal(parent: AbortSignal, timeoutMs: number): DeadlineSignal;
12
+ export declare function throwIfAborted(signal: AbortSignal): void;
13
+ export declare function abortableDelay(milliseconds: number, signal: AbortSignal): Promise<void>;
14
+ export declare function isAbortError(error: unknown): boolean;
15
+ export declare function abortablePromise<Value>(promise: PromiseLike<Value>, signal: AbortSignal): Promise<Value>;
16
+ type StreamReadResult<Value> = Awaited<ReturnType<ReadableStreamDefaultReader<Value>["read"]>>;
17
+ export declare function abortableStreamRead<Value>(reader: ReadableStreamDefaultReader<Value>, signal: AbortSignal): Promise<StreamReadResult<Value>>;
18
+ export {};
19
+ //# sourceMappingURL=abort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,CAAA;IACnB,UAAU,IAAI,OAAO,CAAA;IACrB,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,WAAW,EAAE,GAC9B,iBAAiB,CAsBnB;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,WAAW,EACnB,SAAS,EAAE,MAAM,GAChB,cAAc,CA2BhB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAMxD;AAED,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBvF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKpD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EACpC,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC,EAC3B,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,KAAK,CAAC,CAkChB;AAED,KAAK,gBAAgB,CAAC,KAAK,IAAI,OAAO,CACpC,UAAU,CAAC,2BAA2B,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CACvD,CAAA;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EACvC,MAAM,EAAE,2BAA2B,CAAC,KAAK,CAAC,EAC1C,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAqClC"}
@@ -0,0 +1,121 @@
1
+ export function createLinkedAbortSignal(signals) {
2
+ const controller = new AbortController();
3
+ const listeners = [];
4
+ for (const signal of signals) {
5
+ const relay = () => controller.abort(signal.reason);
6
+ if (signal.aborted) {
7
+ relay();
8
+ break;
9
+ }
10
+ signal.addEventListener("abort", relay, { once: true });
11
+ listeners.push([signal, relay]);
12
+ }
13
+ return {
14
+ signal: controller.signal,
15
+ dispose() {
16
+ for (const [signal, listener] of listeners) {
17
+ signal.removeEventListener("abort", listener);
18
+ }
19
+ },
20
+ };
21
+ }
22
+ export function createDeadlineSignal(parent, timeoutMs) {
23
+ const controller = new AbortController();
24
+ let timedOut = false;
25
+ const abortFromParent = () => {
26
+ controller.abort(parent.reason);
27
+ };
28
+ if (parent.aborted) {
29
+ abortFromParent();
30
+ }
31
+ else {
32
+ parent.addEventListener("abort", abortFromParent, { once: true });
33
+ }
34
+ const timer = setTimeout(() => {
35
+ timedOut = true;
36
+ controller.abort(new DOMException("The operation timed out", "TimeoutError"));
37
+ }, timeoutMs);
38
+ return {
39
+ signal: controller.signal,
40
+ didTimeout: () => timedOut,
41
+ dispose() {
42
+ clearTimeout(timer);
43
+ parent.removeEventListener("abort", abortFromParent);
44
+ },
45
+ };
46
+ }
47
+ export function throwIfAborted(signal) {
48
+ if (signal.aborted) {
49
+ throw signal.reason instanceof Error
50
+ ? signal.reason
51
+ : new DOMException("The operation was aborted", "AbortError");
52
+ }
53
+ }
54
+ export function abortableDelay(milliseconds, signal) {
55
+ throwIfAborted(signal);
56
+ return new Promise((resolve, reject) => {
57
+ const timer = setTimeout(() => {
58
+ signal.removeEventListener("abort", onAbort);
59
+ resolve();
60
+ }, milliseconds);
61
+ const onAbort = () => {
62
+ clearTimeout(timer);
63
+ reject(signal.reason instanceof Error
64
+ ? signal.reason
65
+ : new DOMException("The operation was aborted", "AbortError"));
66
+ };
67
+ signal.addEventListener("abort", onAbort, { once: true });
68
+ });
69
+ }
70
+ export function isAbortError(error) {
71
+ return (error instanceof DOMException &&
72
+ (error.name === "AbortError" || error.name === "TimeoutError"));
73
+ }
74
+ export function abortablePromise(promise, signal) {
75
+ throwIfAborted(signal);
76
+ return new Promise((resolve, reject) => {
77
+ let settled = false;
78
+ const finish = (callback) => {
79
+ if (settled)
80
+ return;
81
+ settled = true;
82
+ signal.removeEventListener("abort", onAbort);
83
+ callback();
84
+ };
85
+ const onAbort = () => {
86
+ finish(() => reject(signal.reason instanceof Error
87
+ ? signal.reason
88
+ : new DOMException("The operation was aborted", "AbortError")));
89
+ };
90
+ signal.addEventListener("abort", onAbort, { once: true });
91
+ void Promise.resolve(promise).then((value) => finish(() => resolve(value)), (error) => finish(() => reject(error instanceof Error
92
+ ? error
93
+ : new Error("The asynchronous operation failed.", { cause: error }))));
94
+ });
95
+ }
96
+ export function abortableStreamRead(reader, signal) {
97
+ throwIfAborted(signal);
98
+ return new Promise((resolve, reject) => {
99
+ let settled = false;
100
+ const finish = (callback) => {
101
+ if (settled)
102
+ return;
103
+ settled = true;
104
+ signal.removeEventListener("abort", onAbort);
105
+ callback();
106
+ };
107
+ const onAbort = () => {
108
+ finish(() => {
109
+ void reader.cancel(signal.reason).catch(() => undefined);
110
+ reject(signal.reason instanceof Error
111
+ ? signal.reason
112
+ : new DOMException("The operation was aborted", "AbortError"));
113
+ });
114
+ };
115
+ signal.addEventListener("abort", onAbort, { once: true });
116
+ void reader.read().then((result) => finish(() => resolve(result)), (error) => finish(() => reject(error instanceof Error
117
+ ? error
118
+ : new Error("The response stream failed.", { cause: error }))));
119
+ });
120
+ }
121
+ //# sourceMappingURL=abort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort.js","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ # Architecture
2
+
3
+ ## Scope and invariants
4
+
5
+ The plugin owns three OpenCode tools: image-to-image editing, text-to-image generation, and balance reporting. Image-to-image is the primary path. It is never silently converted into text-to-image. The configured model, key, and routing mode are not exposed as tool arguments.
6
+
7
+ Core invariants:
8
+
9
+ - all generation uses `POST https://openrouter.ai/api/v1/images`, never `/chat/completions`;
10
+ - model discovery is lazy and occurs before the first paid request;
11
+ - direct mode never initializes KoteGateway;
12
+ - proxy mode routes every OpenRouter endpoint through KoteGateway and fails closed;
13
+ - the package never installs a global fetch interceptor;
14
+ - reference order is preserved exactly;
15
+ - project paths are resolved against `context.worktree`, not `process.cwd()`;
16
+ - source files and existing outputs are never overwritten;
17
+ - secrets, prompts, and base64 are not logged.
18
+
19
+ ## Request flow
20
+
21
+ ```text
22
+ OpenCode tool call
23
+ -> resolve and validate strict plugin options
24
+ -> merge tool arguments over configured defaults
25
+ -> resolve ordered references (edit only)
26
+ -> validate formats, paths, byte limits, and request combinations
27
+ -> discover/cache configured model capabilities
28
+ -> choose direct or per-request proxy transport
29
+ -> OpenRouter Images API
30
+ -> bounded response read and binary validation
31
+ -> atomic worktree writes
32
+ -> OpenCode ToolResult with file attachments
33
+ ```
34
+
35
+ Configuration failures establish a blocked state. Tools may remain registered, but they return `GENIMAGE_CONFIG_INVALID` without network access.
36
+
37
+ ## Reference pipeline
38
+
39
+ Message attachments are resolved only from the parent user message identified through the current `sessionID` and `messageID`. The plugin does not search session history or the project automatically. Explicit project paths are confined to the real worktree. Explicit public references accept HTTPS only and are passed directly to OpenRouter.
40
+
41
+ Local PNG, JPEG, and WebP bytes are validated using magic bytes and encoded in memory as normalized data URLs. No external object store or temporary publishing service is involved. Count, per-file bytes, total local bytes, estimated base64 growth, and final body size are bounded before constructing a large request.
42
+
43
+ ## Model and request validation
44
+
45
+ `GET /images/models` and the capability path returned by the model record determine whether the configured model supports image output, image input, `input_references`, and requested optional parameters. Endpoint paths are not reconstructed from the model ID. Cache keys include model ID, TTL, and only a short SHA-256 key fingerprint.
46
+
47
+ The plugin never changes the configured model. Unsupported capabilities fail before the paid request when discovery data makes that possible.
48
+
49
+ ## Transports
50
+
51
+ Direct mode performs ordinary HTTPS requests to OpenRouter. It never obtains bootstrap data, opens CONNECT, or treats KoteGateway as fallback.
52
+
53
+ Proxy mode reuses KoteGateway core functionality for signed bootstrap, Ed25519 verification, descriptor/cache handling, and sanitized errors. Each plugin-owned OpenRouter request uses the returned per-request proxy. Proxy headers remain separate from target headers. API keys, prompts, base64, and responses stay within TLS to OpenRouter. Any gateway error ends the operation; no direct retry is allowed.
54
+
55
+ Synthetic coexistence tests load `@koteye/kote-gateway-opencode` and this plugin in both orders. They verify one CONNECT for the image request, no image route marker, restoration of the gateway plugin's global wrapper, and native direct transport isolation. Real-runtime compatibility is still gated by the package ranges and OpenCode smoke described in the testing guide.
56
+
57
+ ## HTTP behavior
58
+
59
+ Production base URL is fixed. Redirect mode is manual and all 3xx responses fail. Safe idempotent GET operations may retry temporary statuses up to two times while respecting `Retry-After` and jitter. `POST /images` is never retried because the generation may already have been charged. An ambiguous disconnect becomes `GENIMAGE_REQUEST_STATUS_UNKNOWN`.
60
+
61
+ The caller's abort signal, configured timeout, and transport cancellation are combined. Response bodies are streamed with `maxResponseBytes`; decoded images are bounded by `maxDecodedBytes` before persistence.
62
+
63
+ ## Output and ToolResult
64
+
65
+ Returned base64 is decoded, validated, and classified by actual magic bytes. PNG, JPEG, and WebP are supported; SVG and unknown formats are rejected. A mismatch between requested and actual format changes the output extension and adds a warning. Valid images from a partially invalid response are saved and returned with warnings.
66
+
67
+ Files are written completely to same-directory temporary files and atomically published with a no-clobber hard link. Names avoid collisions; multiple outputs receive numeric suffixes. ToolResult metadata contains safe model/routing/count/path/MIME/size/cost fields, never prompts or base64. Each saved image is also returned as an OpenCode file attachment.
68
+
69
+ ## Balance flow
70
+
71
+ `GET /key` uses the generation key and reports key-specific usage and spending limits. `GET /credits` uses the management key and reports account totals. If the generation key explicitly identifies itself as a management key, it may be reused when no separate management key exists. Credits failure does not discard a successful key response. Balance calls are not cached.
72
+
73
+ ## Lifecycle
74
+
75
+ Import and initial plugin load perform no network requests. The config hook registers the slash command and missing default permissions without overwriting user entries. `dispose` releases only plugin-owned resources. The plugin does not mutate global fetch, provider behavior, MCP, Git, npm, or OpenCode update traffic.
@@ -0,0 +1,110 @@
1
+ # Configuration
2
+
3
+ Plugin options are supplied as the second item in an OpenCode plugin tuple. The public JSON Schema is [`schema/plugin-options.schema.json`](../schema/plugin-options.schema.json).
4
+
5
+ ```jsonc
6
+ {
7
+ "plugin": [
8
+ [
9
+ "openrouter-genimage-kote-opencode@0.1.0",
10
+ {
11
+ "routing": "direct",
12
+ "model": "openai/gpt-image-1",
13
+ "apiKey": "{env:OPENROUTER_API_KEY}"
14
+ }
15
+ ]
16
+ ]
17
+ }
18
+ ```
19
+
20
+ Unknown fields are errors at every configuration object level. `baseUrl`, `openrouterUrl`, and `apiUrl` are intentionally unsupported; production traffic is fixed to `https://openrouter.ai/api/v1`.
21
+
22
+ ## Options
23
+
24
+ | Option | Required | Default | Constraints |
25
+ |---|---:|---|---|
26
+ | `routing` | yes | — | `direct` or `proxy` |
27
+ | `model` | yes | — | non-empty after trimming |
28
+ | `apiKey` | no | `OPENROUTER_API_KEY` | non-empty after trimming |
29
+ | `managementApiKey` | no | `OPENROUTER_MANAGEMENT_API_KEY` | non-empty after trimming |
30
+ | `outputDirectory` | no | `assets/generated` | relative worktree directory |
31
+ | `requestTimeoutMs` | no | `300000` | positive integer |
32
+ | `modelCacheTtlMs` | no | `3600000` | positive integer |
33
+ | `maxResponseBytes` | no | `134217728` | positive integer |
34
+ | `maxDecodedBytes` | no | `134217728` | positive integer |
35
+
36
+ `apiKey` resolution is options first, environment second, then `GENIMAGE_OPENROUTER_KEY_MISSING`. The management key uses the same precedence but may remain absent; only full account credits become unavailable.
37
+
38
+ ## Generation defaults
39
+
40
+ Values in `defaults` apply to both image tools. A tool argument overrides the corresponding default; absent values are omitted from the OpenRouter request.
41
+
42
+ | Option | Default | Constraints |
43
+ |---|---|---|
44
+ | `aspect_ratio` | omitted | `1:1`, `1:2`, `1:4`, `1:8`, `2:1`, `2:3`, `3:2`, `3:4`, `4:1`, `4:3`, `4:5`, `5:4`, `8:1`, `9:16`, `16:9`, `9:19.5`, `19.5:9`, `9:20`, `20:9`, `9:21`, `21:9`, `auto` |
45
+ | `background` | omitted | `auto`, `transparent`, `opaque` |
46
+ | `n` | `1` | integer, 1–10 |
47
+ | `output_format` | omitted | `png`, `jpeg`, `webp` |
48
+ | `output_compression` | omitted | integer, 0–100; JPEG/WebP only |
49
+ | `quality` | omitted | `auto`, `low`, `medium`, `high` |
50
+ | `resolution` | omitted | `512`, `1K`, `2K`, `4K` |
51
+ | `seed` | omitted | non-negative integer |
52
+
53
+ `background: "transparent"` is valid only with PNG or WebP. `output_compression` is valid only with JPEG or WebP. Runtime validation checks these cross-field combinations before network access. Model discovery may reject otherwise valid options when the configured endpoint does not support them.
54
+
55
+ ## Input limits
56
+
57
+ | Option | Default | Constraints |
58
+ |---|---:|---|
59
+ | `input.maxReferences` | `16` | integer, 1–16 |
60
+ | `input.maxFileBytes` | `20971520` | positive integer |
61
+ | `input.maxTotalBytes` | `52428800` | positive integer and not less than `maxFileBytes` |
62
+
63
+ Limits apply before constructing a large base64 JSON request. The implementation also accounts for base64 expansion and final request-body size.
64
+
65
+ Standard JSON Schema cannot compare two arbitrary sibling numeric values. Consequently, the published schema validates both byte fields as positive integers but cannot express `maxTotalBytes >= maxFileBytes`; strict runtime validation enforces that relationship. Likewise, trim-aware non-empty checks and output-format combinations remain runtime checks in addition to schema validation.
66
+
67
+ ## Complete example
68
+
69
+ ```jsonc
70
+ {
71
+ "$schema": "https://opencode.ai/config.json",
72
+ "plugin": [
73
+ [
74
+ "openrouter-genimage-kote-opencode@0.1.0",
75
+ {
76
+ "routing": "proxy",
77
+ "model": "openai/gpt-image-1",
78
+ "apiKey": "{env:OPENROUTER_API_KEY}",
79
+ "managementApiKey": "{env:OPENROUTER_MANAGEMENT_API_KEY}",
80
+ "outputDirectory": "assets/generated",
81
+ "defaults": {
82
+ "aspect_ratio": "auto",
83
+ "background": "auto",
84
+ "n": 1,
85
+ "output_format": "png",
86
+ "quality": "auto"
87
+ },
88
+ "input": {
89
+ "maxReferences": 16,
90
+ "maxFileBytes": 20971520,
91
+ "maxTotalBytes": 52428800
92
+ },
93
+ "requestTimeoutMs": 300000,
94
+ "modelCacheTtlMs": 3600000,
95
+ "maxResponseBytes": 134217728,
96
+ "maxDecodedBytes": 134217728
97
+ }
98
+ ]
99
+ ],
100
+ "permission": {
101
+ "openrouter_edit_image": "ask",
102
+ "openrouter_generate_image": "ask",
103
+ "openrouter_balance": "allow"
104
+ }
105
+ }
106
+ ```
107
+
108
+ ## Invalid configuration behavior
109
+
110
+ Invalid options establish a blocked configuration state and return `GENIMAGE_CONFIG_INVALID`. Tools may still be visible, but no network request is allowed. Examples include missing routing/model, blank strings, unknown fields, invalid numeric limits, `maxTotalBytes < maxFileBytes`, unsupported enum values, and forbidden custom API URL fields.