kimaki 0.4.94 → 0.4.95
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 +23 -18
- package/dist/discord-utils.js +5 -2
- package/dist/utils.js +5 -1
- package/package.json +4 -4
- package/src/anthropic-auth-plugin.ts +22 -17
- package/src/discord-utils.ts +19 -17
- package/src/utils.ts +5 -1
|
@@ -430,15 +430,25 @@ function buildAuthorizeHandler(mode) {
|
|
|
430
430
|
function toClaudeCodeToolName(name) {
|
|
431
431
|
return OPENCODE_TO_CLAUDE_CODE_TOOL_NAME[name.toLowerCase()] ?? name;
|
|
432
432
|
}
|
|
433
|
-
function sanitizeSystemText(text) {
|
|
434
|
-
|
|
433
|
+
function sanitizeSystemText(text, onError) {
|
|
434
|
+
const startIdx = text.indexOf(OPENCODE_IDENTITY);
|
|
435
|
+
if (startIdx === -1)
|
|
436
|
+
return text;
|
|
437
|
+
const codeRefsMarker = '# Code References';
|
|
438
|
+
const endIdx = text.indexOf(codeRefsMarker, startIdx);
|
|
439
|
+
if (endIdx === -1) {
|
|
440
|
+
onError?.(`sanitizeSystemText: could not find '# Code References' after OpenCode identity`);
|
|
441
|
+
return text;
|
|
442
|
+
}
|
|
443
|
+
// Remove everything from the OpenCode identity up to (but not including) '# Code References'
|
|
444
|
+
return text.slice(0, startIdx) + text.slice(endIdx);
|
|
435
445
|
}
|
|
436
|
-
function prependClaudeCodeIdentity(system) {
|
|
446
|
+
function prependClaudeCodeIdentity(system, onError) {
|
|
437
447
|
const identityBlock = { type: 'text', text: CLAUDE_CODE_IDENTITY };
|
|
438
448
|
if (typeof system === 'undefined')
|
|
439
449
|
return [identityBlock];
|
|
440
450
|
if (typeof system === 'string') {
|
|
441
|
-
const sanitized = sanitizeSystemText(system);
|
|
451
|
+
const sanitized = sanitizeSystemText(system, onError);
|
|
442
452
|
if (sanitized === CLAUDE_CODE_IDENTITY)
|
|
443
453
|
return [identityBlock];
|
|
444
454
|
return [identityBlock, { type: 'text', text: sanitized }];
|
|
@@ -447,11 +457,11 @@ function prependClaudeCodeIdentity(system) {
|
|
|
447
457
|
return [identityBlock, system];
|
|
448
458
|
const sanitized = system.map((item) => {
|
|
449
459
|
if (typeof item === 'string')
|
|
450
|
-
return { type: 'text', text: sanitizeSystemText(item) };
|
|
460
|
+
return { type: 'text', text: sanitizeSystemText(item, onError) };
|
|
451
461
|
if (item && typeof item === 'object' && item.type === 'text') {
|
|
452
462
|
const text = item.text;
|
|
453
463
|
if (typeof text === 'string') {
|
|
454
|
-
return { ...item, text: sanitizeSystemText(text) };
|
|
464
|
+
return { ...item, text: sanitizeSystemText(text, onError) };
|
|
455
465
|
}
|
|
456
466
|
}
|
|
457
467
|
return item;
|
|
@@ -465,7 +475,7 @@ function prependClaudeCodeIdentity(system) {
|
|
|
465
475
|
}
|
|
466
476
|
return [identityBlock, ...sanitized];
|
|
467
477
|
}
|
|
468
|
-
function rewriteRequestPayload(body) {
|
|
478
|
+
function rewriteRequestPayload(body, onError) {
|
|
469
479
|
if (!body)
|
|
470
480
|
return { body, modelId: undefined, reverseToolNameMap: new Map() };
|
|
471
481
|
try {
|
|
@@ -486,7 +496,7 @@ function rewriteRequestPayload(body) {
|
|
|
486
496
|
});
|
|
487
497
|
}
|
|
488
498
|
// Rename system prompt
|
|
489
|
-
payload.system = prependClaudeCodeIdentity(payload.system);
|
|
499
|
+
payload.system = prependClaudeCodeIdentity(payload.system, onError);
|
|
490
500
|
// Rename tool_choice
|
|
491
501
|
if (payload.tool_choice &&
|
|
492
502
|
typeof payload.tool_choice === 'object' &&
|
|
@@ -627,15 +637,6 @@ async function getFreshOAuth(getAuth, client) {
|
|
|
627
637
|
// --- Plugin export ---
|
|
628
638
|
const AnthropicAuthPlugin = async ({ client }) => {
|
|
629
639
|
return {
|
|
630
|
-
"experimental.chat.system.transform": async (input, output) => {
|
|
631
|
-
if (input.model.providerID !== ('anthropic'))
|
|
632
|
-
return;
|
|
633
|
-
const opencodePromptPart = output.system.findIndex(x => x?.includes('https://github.com/anomalyco/opencode'));
|
|
634
|
-
// Remove the OpenCode system prompt part if present
|
|
635
|
-
if (opencodePromptPart !== -1) {
|
|
636
|
-
output.system.splice(opencodePromptPart, 1);
|
|
637
|
-
}
|
|
638
|
-
},
|
|
639
640
|
auth: {
|
|
640
641
|
provider: 'anthropic',
|
|
641
642
|
async loader(getAuth, provider) {
|
|
@@ -667,7 +668,11 @@ const AnthropicAuthPlugin = async ({ client }) => {
|
|
|
667
668
|
.text()
|
|
668
669
|
.catch(() => undefined)
|
|
669
670
|
: undefined;
|
|
670
|
-
const rewritten = rewriteRequestPayload(originalBody)
|
|
671
|
+
const rewritten = rewriteRequestPayload(originalBody, (msg) => {
|
|
672
|
+
client.tui.showToast({
|
|
673
|
+
body: { message: msg, variant: 'error' },
|
|
674
|
+
}).catch(() => { });
|
|
675
|
+
});
|
|
671
676
|
const headers = new Headers(init?.headers);
|
|
672
677
|
if (input instanceof Request) {
|
|
673
678
|
input.headers.forEach((v, k) => {
|
package/dist/discord-utils.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// Discord-specific utility functions.
|
|
2
2
|
// Handles markdown splitting for Discord's 2000-char limit, code block escaping,
|
|
3
3
|
// thread message sending, and channel metadata extraction from topic tags.
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// Use namespace import for CJS interop — discord.js is CJS and its named
|
|
5
|
+
// exports aren't detectable by all ESM loaders (e.g. tsx/esbuild) because
|
|
6
|
+
// discord.js uses tslib's __exportStar which is opaque to static analysis.
|
|
7
|
+
import * as discord from 'discord.js';
|
|
8
|
+
const { ChannelType, GuildMember, MessageFlags, PermissionsBitField, REST, Routes } = discord;
|
|
6
9
|
import { discordApiUrl } from './discord-urls.js';
|
|
7
10
|
import { Lexer } from 'marked';
|
|
8
11
|
import { splitTablesFromMarkdown } from './format-tables.js';
|
package/dist/utils.js
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
// Includes Discord OAuth URL generation, array deduplication,
|
|
3
3
|
// abort error detection, and date/time formatting helpers.
|
|
4
4
|
import os from 'node:os';
|
|
5
|
-
import
|
|
5
|
+
// Use namespace import for CJS interop — discord.js is CJS and its named
|
|
6
|
+
// exports aren't detectable by all ESM loaders (e.g. tsx/esbuild) because
|
|
7
|
+
// discord.js uses tslib's __exportStar which is opaque to static analysis.
|
|
8
|
+
import * as discord from 'discord.js';
|
|
9
|
+
const { PermissionsBitField } = discord;
|
|
6
10
|
import * as errore from 'errore';
|
|
7
11
|
export function generateBotInstallUrl({ clientId, permissions = [
|
|
8
12
|
PermissionsBitField.Flags.ViewChannel,
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "kimaki",
|
|
3
3
|
"module": "index.ts",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.4.
|
|
5
|
+
"version": "0.4.95",
|
|
6
6
|
"repository": "https://github.com/remorses/kimaki",
|
|
7
7
|
"bin": "bin.js",
|
|
8
8
|
"files": [
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"prisma": "7.4.2",
|
|
26
26
|
"tsx": "^4.20.5",
|
|
27
27
|
"undici": "^8.0.2",
|
|
28
|
+
"discord-digital-twin": "^0.1.0",
|
|
28
29
|
"opencode-cached-provider": "^0.0.1",
|
|
29
30
|
"db": "^0.0.0",
|
|
30
|
-
"discord-digital-twin": "^0.1.0",
|
|
31
31
|
"opencode-deterministic-provider": "^0.0.1"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"zod": "^4.3.6",
|
|
66
66
|
"zustand": "^5.0.11",
|
|
67
67
|
"errore": "^0.14.1",
|
|
68
|
-
"traforo": "^0.2.4",
|
|
69
68
|
"opencode-injection-guard": "^0.2.1",
|
|
70
|
-
"libsqlproxy": "^0.1.0"
|
|
69
|
+
"libsqlproxy": "^0.1.0",
|
|
70
|
+
"traforo": "^0.2.4"
|
|
71
71
|
},
|
|
72
72
|
"optionalDependencies": {
|
|
73
73
|
"@snazzah/davey": "^0.1.10",
|
|
@@ -528,17 +528,26 @@ function toClaudeCodeToolName(name: string) {
|
|
|
528
528
|
return OPENCODE_TO_CLAUDE_CODE_TOOL_NAME[name.toLowerCase()] ?? name
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
-
function sanitizeSystemText(text: string) {
|
|
532
|
-
|
|
531
|
+
function sanitizeSystemText(text: string, onError?: (msg: string) => void) {
|
|
532
|
+
const startIdx = text.indexOf(OPENCODE_IDENTITY)
|
|
533
|
+
if (startIdx === -1) return text
|
|
534
|
+
const codeRefsMarker = '# Code References'
|
|
535
|
+
const endIdx = text.indexOf(codeRefsMarker, startIdx)
|
|
536
|
+
if (endIdx === -1) {
|
|
537
|
+
onError?.(`sanitizeSystemText: could not find '# Code References' after OpenCode identity`)
|
|
538
|
+
return text
|
|
539
|
+
}
|
|
540
|
+
// Remove everything from the OpenCode identity up to (but not including) '# Code References'
|
|
541
|
+
return text.slice(0, startIdx) + text.slice(endIdx)
|
|
533
542
|
}
|
|
534
543
|
|
|
535
|
-
function prependClaudeCodeIdentity(system: unknown) {
|
|
544
|
+
function prependClaudeCodeIdentity(system: unknown, onError?: (msg: string) => void) {
|
|
536
545
|
const identityBlock = { type: 'text', text: CLAUDE_CODE_IDENTITY }
|
|
537
546
|
|
|
538
547
|
if (typeof system === 'undefined') return [identityBlock]
|
|
539
548
|
|
|
540
549
|
if (typeof system === 'string') {
|
|
541
|
-
const sanitized = sanitizeSystemText(system)
|
|
550
|
+
const sanitized = sanitizeSystemText(system, onError)
|
|
542
551
|
if (sanitized === CLAUDE_CODE_IDENTITY) return [identityBlock]
|
|
543
552
|
return [identityBlock, { type: 'text', text: sanitized }]
|
|
544
553
|
}
|
|
@@ -546,11 +555,11 @@ function prependClaudeCodeIdentity(system: unknown) {
|
|
|
546
555
|
if (!Array.isArray(system)) return [identityBlock, system]
|
|
547
556
|
|
|
548
557
|
const sanitized = system.map((item) => {
|
|
549
|
-
if (typeof item === 'string') return { type: 'text', text: sanitizeSystemText(item) }
|
|
558
|
+
if (typeof item === 'string') return { type: 'text', text: sanitizeSystemText(item, onError) }
|
|
550
559
|
if (item && typeof item === 'object' && (item as { type?: unknown }).type === 'text') {
|
|
551
560
|
const text = (item as { text?: unknown }).text
|
|
552
561
|
if (typeof text === 'string') {
|
|
553
|
-
return { ...(item as Record<string, unknown>), text: sanitizeSystemText(text) }
|
|
562
|
+
return { ...(item as Record<string, unknown>), text: sanitizeSystemText(text, onError) }
|
|
554
563
|
}
|
|
555
564
|
}
|
|
556
565
|
return item
|
|
@@ -568,7 +577,7 @@ function prependClaudeCodeIdentity(system: unknown) {
|
|
|
568
577
|
return [identityBlock, ...sanitized]
|
|
569
578
|
}
|
|
570
579
|
|
|
571
|
-
function rewriteRequestPayload(body: string | undefined) {
|
|
580
|
+
function rewriteRequestPayload(body: string | undefined, onError?: (msg: string) => void) {
|
|
572
581
|
if (!body) return { body, modelId: undefined, reverseToolNameMap: new Map<string, string>() }
|
|
573
582
|
|
|
574
583
|
try {
|
|
@@ -589,7 +598,7 @@ function rewriteRequestPayload(body: string | undefined) {
|
|
|
589
598
|
}
|
|
590
599
|
|
|
591
600
|
// Rename system prompt
|
|
592
|
-
payload.system = prependClaudeCodeIdentity(payload.system)
|
|
601
|
+
payload.system = prependClaudeCodeIdentity(payload.system, onError)
|
|
593
602
|
|
|
594
603
|
// Rename tool_choice
|
|
595
604
|
if (
|
|
@@ -743,14 +752,6 @@ async function getFreshOAuth(
|
|
|
743
752
|
|
|
744
753
|
const AnthropicAuthPlugin: Plugin = async ({ client }) => {
|
|
745
754
|
return {
|
|
746
|
-
"experimental.chat.system.transform": async (input, output) => {
|
|
747
|
-
if (input.model.providerID !== ('anthropic')) return
|
|
748
|
-
const opencodePromptPart = output.system.findIndex(x => x?.includes('https://github.com/anomalyco/opencode'))
|
|
749
|
-
// Remove the OpenCode system prompt part if present
|
|
750
|
-
if (opencodePromptPart !== -1) {
|
|
751
|
-
output.system.splice(opencodePromptPart, 1)
|
|
752
|
-
}
|
|
753
|
-
},
|
|
754
755
|
auth: {
|
|
755
756
|
provider: 'anthropic',
|
|
756
757
|
async loader(
|
|
@@ -787,7 +788,11 @@ const AnthropicAuthPlugin: Plugin = async ({ client }) => {
|
|
|
787
788
|
.catch(() => undefined)
|
|
788
789
|
: undefined
|
|
789
790
|
|
|
790
|
-
const rewritten = rewriteRequestPayload(originalBody)
|
|
791
|
+
const rewritten = rewriteRequestPayload(originalBody, (msg) => {
|
|
792
|
+
client.tui.showToast({
|
|
793
|
+
body: { message: msg, variant: 'error' },
|
|
794
|
+
}).catch(() => {})
|
|
795
|
+
})
|
|
791
796
|
const headers = new Headers(init?.headers)
|
|
792
797
|
if (input instanceof Request) {
|
|
793
798
|
input.headers.forEach((v, k) => {
|
package/src/discord-utils.ts
CHANGED
|
@@ -2,19 +2,21 @@
|
|
|
2
2
|
// Handles markdown splitting for Discord's 2000-char limit, code block escaping,
|
|
3
3
|
// thread message sending, and channel metadata extraction from topic tags.
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
// Use namespace import for CJS interop — discord.js is CJS and its named
|
|
6
|
+
// exports aren't detectable by all ESM loaders (e.g. tsx/esbuild) because
|
|
7
|
+
// discord.js uses tslib's __exportStar which is opaque to static analysis.
|
|
8
|
+
import * as discord from 'discord.js'
|
|
9
|
+
import type {
|
|
10
|
+
APIInteractionGuildMember,
|
|
11
|
+
AutocompleteInteraction,
|
|
12
|
+
GuildMember as GuildMemberType,
|
|
13
|
+
Guild,
|
|
14
|
+
Message,
|
|
15
|
+
REST as RESTType,
|
|
16
|
+
TextChannel,
|
|
17
|
+
ThreadChannel,
|
|
16
18
|
} from 'discord.js'
|
|
17
|
-
|
|
19
|
+
const { ChannelType, GuildMember, MessageFlags, PermissionsBitField, REST, Routes } = discord
|
|
18
20
|
import type { OpencodeClient } from '@opencode-ai/sdk/v2'
|
|
19
21
|
import { discordApiUrl } from './discord-urls.js'
|
|
20
22
|
import { Lexer } from 'marked'
|
|
@@ -37,7 +39,7 @@ const discordLogger = createLogger(LogPrefix.DISCORD)
|
|
|
37
39
|
* Returns false if member is null or has the "no-kimaki" role (overrides all).
|
|
38
40
|
*/
|
|
39
41
|
export function hasKimakiBotPermission(
|
|
40
|
-
member:
|
|
42
|
+
member: GuildMemberType | APIInteractionGuildMember | null,
|
|
41
43
|
guild?: Guild | null,
|
|
42
44
|
): boolean {
|
|
43
45
|
if (!member) {
|
|
@@ -61,7 +63,7 @@ export function hasKimakiBotPermission(
|
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
function hasRoleByName(
|
|
64
|
-
member:
|
|
66
|
+
member: GuildMemberType | APIInteractionGuildMember,
|
|
65
67
|
roleName: string,
|
|
66
68
|
guild?: Guild | null,
|
|
67
69
|
): boolean {
|
|
@@ -89,7 +91,7 @@ function hasRoleByName(
|
|
|
89
91
|
* Check if the member has the "no-kimaki" role that blocks bot access.
|
|
90
92
|
* Separate from hasKimakiBotPermission so callers can show a specific error message.
|
|
91
93
|
*/
|
|
92
|
-
export function hasNoKimakiRole(member:
|
|
94
|
+
export function hasNoKimakiRole(member: GuildMemberType | null): boolean {
|
|
93
95
|
if (!member?.roles?.cache) {
|
|
94
96
|
return false
|
|
95
97
|
}
|
|
@@ -108,7 +110,7 @@ export async function reactToThread({
|
|
|
108
110
|
channelId,
|
|
109
111
|
emoji,
|
|
110
112
|
}: {
|
|
111
|
-
rest:
|
|
113
|
+
rest: RESTType
|
|
112
114
|
threadId: string
|
|
113
115
|
/** Parent channel ID where the thread starter message lives.
|
|
114
116
|
* If not provided, fetches the thread info from Discord API to resolve it. */
|
|
@@ -169,7 +171,7 @@ export async function archiveThread({
|
|
|
169
171
|
client,
|
|
170
172
|
archiveDelay = 0,
|
|
171
173
|
}: {
|
|
172
|
-
rest:
|
|
174
|
+
rest: RESTType
|
|
173
175
|
threadId: string
|
|
174
176
|
parentChannelId?: string
|
|
175
177
|
sessionId?: string
|
package/src/utils.ts
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
// abort error detection, and date/time formatting helpers.
|
|
4
4
|
|
|
5
5
|
import os from 'node:os'
|
|
6
|
-
import
|
|
6
|
+
// Use namespace import for CJS interop — discord.js is CJS and its named
|
|
7
|
+
// exports aren't detectable by all ESM loaders (e.g. tsx/esbuild) because
|
|
8
|
+
// discord.js uses tslib's __exportStar which is opaque to static analysis.
|
|
9
|
+
import * as discord from 'discord.js'
|
|
10
|
+
const { PermissionsBitField } = discord
|
|
7
11
|
import type { BotMode } from './database.js'
|
|
8
12
|
import * as errore from 'errore'
|
|
9
13
|
|