kimaki 0.4.80 → 0.4.82
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/anthropic-auth-plugin.js +114 -72
- package/dist/cli.js +2 -499
- package/dist/commands/memory-snapshot.js +24 -0
- package/dist/commands/restart-opencode-server.js +61 -8
- package/dist/condense-memory.js +1 -1
- package/dist/context-awareness-plugin.js +1 -1
- package/dist/discord-bot.js +11 -4
- package/dist/discord-command-registration.js +512 -0
- package/dist/interaction-handler.js +7 -0
- package/dist/ipc-polling.js +4 -3
- package/dist/ipc-tools-plugin.js +1 -1
- package/dist/kimaki-opencode-plugin-loading.e2e.test.js +80 -0
- package/dist/kimaki-opencode-plugin.js +13 -0
- package/dist/kimaki-opencode-plugin.test.js +98 -0
- package/dist/opencode.js +1 -1
- package/dist/runtime-idle-sweeper.js +3 -1
- package/dist/sentry.js +1 -1
- package/dist/session-handler/thread-session-runtime.js +71 -14
- package/package.json +4 -4
- package/src/anthropic-auth-plugin.ts +121 -70
- package/src/cli.ts +2 -658
- package/src/commands/memory-snapshot.ts +30 -0
- package/src/commands/restart-opencode-server.ts +67 -7
- package/src/condense-memory.ts +1 -1
- package/src/context-awareness-plugin.ts +1 -1
- package/src/discord-bot.ts +14 -4
- package/src/discord-command-registration.ts +678 -0
- package/src/interaction-handler.ts +8 -0
- package/src/ipc-polling.ts +4 -3
- package/src/ipc-tools-plugin.ts +1 -1
- package/src/{opencode-plugin-loading.e2e.test.ts → kimaki-opencode-plugin-loading.e2e.test.ts} +1 -1
- package/src/opencode.ts +1 -1
- package/src/runtime-idle-sweeper.ts +3 -1
- package/src/sentry.ts +1 -1
- package/src/session-handler/thread-session-runtime.ts +87 -15
- package/skills/lintcn/SKILL.md +0 -749
- /package/src/{opencode-plugin.test.ts → kimaki-opencode-plugin.test.ts} +0 -0
- /package/src/{opencode-plugin.ts → kimaki-opencode-plugin.ts} +0 -0
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* - https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/providers/anthropic.ts
|
|
16
16
|
*/
|
|
17
17
|
import { generatePKCE } from "@openauthjs/openauth/pkce";
|
|
18
|
+
import { spawn } from "node:child_process";
|
|
18
19
|
import * as fs from "node:fs/promises";
|
|
19
20
|
import { createServer } from "node:http";
|
|
20
21
|
import { homedir } from "node:os";
|
|
@@ -62,37 +63,88 @@ const OPENCODE_TO_CLAUDE_CODE_TOOL_NAME = {
|
|
|
62
63
|
write: "Write",
|
|
63
64
|
};
|
|
64
65
|
// --- HTTP helpers ---
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
// Claude OAuth token exchange can 429 when this runs inside the opencode auth
|
|
67
|
+
// process, even with the same payload that succeeds in a plain Node process.
|
|
68
|
+
// Run these OAuth-only HTTP calls in an isolated Node child to avoid whatever
|
|
69
|
+
// parent-process runtime state is affecting the in-process requests.
|
|
70
|
+
async function requestText(urlString, options) {
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
const payload = JSON.stringify({
|
|
73
|
+
body: options.body,
|
|
74
|
+
headers: options.headers,
|
|
75
|
+
method: options.method,
|
|
76
|
+
url: urlString,
|
|
77
|
+
});
|
|
78
|
+
const child = spawn("node", ["-e", `
|
|
79
|
+
const input = JSON.parse(process.argv[1]);
|
|
80
|
+
(async () => {
|
|
81
|
+
const response = await fetch(input.url, {
|
|
82
|
+
method: input.method,
|
|
83
|
+
headers: input.headers,
|
|
84
|
+
body: input.body,
|
|
85
|
+
});
|
|
86
|
+
const text = await response.text();
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
console.error(JSON.stringify({ status: response.status, body: text }));
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
process.stdout.write(text);
|
|
92
|
+
})().catch((error) => {
|
|
93
|
+
console.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
94
|
+
process.exit(1);
|
|
95
|
+
});
|
|
96
|
+
`.trim(), payload], {
|
|
97
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
98
|
+
});
|
|
99
|
+
let stdout = "";
|
|
100
|
+
let stderr = "";
|
|
101
|
+
const timeout = setTimeout(() => {
|
|
102
|
+
child.kill();
|
|
103
|
+
reject(new Error(`Request timed out. url=${urlString}`));
|
|
104
|
+
}, 30_000);
|
|
105
|
+
child.stdout.on("data", (chunk) => {
|
|
106
|
+
stdout += String(chunk);
|
|
107
|
+
});
|
|
108
|
+
child.stderr.on("data", (chunk) => {
|
|
109
|
+
stderr += String(chunk);
|
|
110
|
+
});
|
|
111
|
+
child.on("error", (error) => {
|
|
112
|
+
clearTimeout(timeout);
|
|
113
|
+
reject(error);
|
|
114
|
+
});
|
|
115
|
+
child.on("close", (code) => {
|
|
116
|
+
clearTimeout(timeout);
|
|
117
|
+
if (code !== 0) {
|
|
118
|
+
let details = stderr.trim();
|
|
119
|
+
try {
|
|
120
|
+
const parsed = JSON.parse(details);
|
|
121
|
+
if (typeof parsed.status === "number") {
|
|
122
|
+
reject(new Error(`HTTP ${parsed.status} from ${urlString}: ${parsed.body ?? ""}`));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// fall back to raw stderr
|
|
128
|
+
}
|
|
129
|
+
reject(new Error(details || `Node helper exited with code ${code}`));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
resolve(stdout);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
69
135
|
}
|
|
70
136
|
async function postJson(url, body) {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const delayMs = retryAfter
|
|
83
|
-
? Math.min(Number(retryAfter) * 1000, 60_000)
|
|
84
|
-
: BASE_DELAY_MS * 2 ** attempt;
|
|
85
|
-
console.warn(`[anthropic-auth] 429 from ${url}, retrying in ${delayMs}ms (attempt ${attempt + 1}/${MAX_RETRIES})`);
|
|
86
|
-
await sleep(delayMs);
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
if (!response.ok) {
|
|
90
|
-
const text = await response.text().catch(() => "");
|
|
91
|
-
throw new Error(`HTTP ${response.status} from ${url}: ${text}`);
|
|
92
|
-
}
|
|
93
|
-
return response.json();
|
|
94
|
-
}
|
|
95
|
-
throw new Error(`Exhausted retries for ${url}`);
|
|
137
|
+
const requestBody = JSON.stringify(body);
|
|
138
|
+
const responseText = await requestText(url, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: {
|
|
141
|
+
Accept: "application/json",
|
|
142
|
+
"Content-Length": String(Buffer.byteLength(requestBody)),
|
|
143
|
+
"Content-Type": "application/json",
|
|
144
|
+
},
|
|
145
|
+
body: requestBody,
|
|
146
|
+
});
|
|
147
|
+
return JSON.parse(responseText);
|
|
96
148
|
}
|
|
97
149
|
// --- File lock for token refresh ---
|
|
98
150
|
let pendingRefresh;
|
|
@@ -163,33 +215,16 @@ async function refreshAnthropicToken(refreshToken) {
|
|
|
163
215
|
};
|
|
164
216
|
}
|
|
165
217
|
async function createApiKey(accessToken) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (response.status === 429 && attempt < MAX_RETRIES) {
|
|
177
|
-
const retryAfter = response.headers.get("retry-after");
|
|
178
|
-
const delayMs = retryAfter
|
|
179
|
-
? Math.min(Number(retryAfter) * 1000, 60_000)
|
|
180
|
-
: BASE_DELAY_MS * 2 ** attempt;
|
|
181
|
-
console.warn(`[anthropic-auth] 429 creating API key, retrying in ${delayMs}ms (attempt ${attempt + 1}/${MAX_RETRIES})`);
|
|
182
|
-
await sleep(delayMs);
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
if (!response.ok) {
|
|
186
|
-
const text = await response.text().catch(() => "");
|
|
187
|
-
throw new Error(`HTTP ${response.status} creating API key: ${text}`);
|
|
188
|
-
}
|
|
189
|
-
const json = (await response.json());
|
|
190
|
-
return { type: "success", key: json.raw_key };
|
|
191
|
-
}
|
|
192
|
-
throw new Error(`Exhausted retries for ${CREATE_API_KEY_URL}`);
|
|
218
|
+
const responseText = await requestText(CREATE_API_KEY_URL, {
|
|
219
|
+
method: "POST",
|
|
220
|
+
headers: {
|
|
221
|
+
Accept: "application/json",
|
|
222
|
+
authorization: `Bearer ${accessToken}`,
|
|
223
|
+
"Content-Type": "application/json",
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
const json = JSON.parse(responseText);
|
|
227
|
+
return { type: "success", key: json.raw_key };
|
|
193
228
|
}
|
|
194
229
|
async function startCallbackServer(expectedState) {
|
|
195
230
|
return new Promise((resolve, reject) => {
|
|
@@ -317,6 +352,7 @@ function buildAuthorizeHandler(mode) {
|
|
|
317
352
|
return async () => {
|
|
318
353
|
const auth = await beginAuthorizationFlow();
|
|
319
354
|
const isRemote = Boolean(process.env.KIMAKI);
|
|
355
|
+
let pendingAuthResult;
|
|
320
356
|
const finalize = async (result) => {
|
|
321
357
|
const verifier = auth.verifier;
|
|
322
358
|
const creds = await exchangeAuthorizationCode(result.code, result.state || verifier, verifier, REDIRECT_URI);
|
|
@@ -331,14 +367,17 @@ function buildAuthorizeHandler(mode) {
|
|
|
331
367
|
instructions: "Complete login in your browser on this machine. OpenCode will catch the localhost callback automatically.",
|
|
332
368
|
method: "auto",
|
|
333
369
|
callback: async () => {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
370
|
+
pendingAuthResult ??= (async () => {
|
|
371
|
+
try {
|
|
372
|
+
const result = await waitForCallback(auth.callbackServer);
|
|
373
|
+
return await finalize(result);
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
console.error(`[anthropic-auth] ${error}`);
|
|
377
|
+
return { type: "failed" };
|
|
378
|
+
}
|
|
379
|
+
})();
|
|
380
|
+
return pendingAuthResult;
|
|
342
381
|
},
|
|
343
382
|
};
|
|
344
383
|
}
|
|
@@ -347,14 +386,17 @@ function buildAuthorizeHandler(mode) {
|
|
|
347
386
|
instructions: "Complete login in your browser, then paste the final redirect URL from the address bar here. Pasting just the authorization code also works.",
|
|
348
387
|
method: "code",
|
|
349
388
|
callback: async (input) => {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
389
|
+
pendingAuthResult ??= (async () => {
|
|
390
|
+
try {
|
|
391
|
+
const result = await waitForCallback(auth.callbackServer, input);
|
|
392
|
+
return await finalize(result);
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
console.error(`[anthropic-auth] ${error}`);
|
|
396
|
+
return { type: "failed" };
|
|
397
|
+
}
|
|
398
|
+
})();
|
|
399
|
+
return pendingAuthResult;
|
|
358
400
|
},
|
|
359
401
|
};
|
|
360
402
|
};
|