kimaki 0.4.40 → 0.4.42
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/LICENSE +21 -0
- package/dist/cli.js +89 -30
- package/package.json +14 -15
- package/src/cli.ts +146 -55
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Kimaki
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/cli.js
CHANGED
|
@@ -292,6 +292,79 @@ async function registerCommands({ token, appId, userCommands = [], agents = [],
|
|
|
292
292
|
throw error;
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Store channel-directory mappings in the database.
|
|
297
|
+
* Called after Discord login to persist channel configurations.
|
|
298
|
+
*/
|
|
299
|
+
function storeChannelDirectories({ kimakiChannels, db, }) {
|
|
300
|
+
for (const { guild, channels } of kimakiChannels) {
|
|
301
|
+
for (const channel of channels) {
|
|
302
|
+
if (channel.kimakiDirectory) {
|
|
303
|
+
db.prepare('INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)').run(channel.id, channel.kimakiDirectory, 'text', channel.kimakiApp || null);
|
|
304
|
+
const voiceChannel = guild.channels.cache.find((ch) => ch.type === ChannelType.GuildVoice && ch.name === channel.name);
|
|
305
|
+
if (voiceChannel) {
|
|
306
|
+
db.prepare('INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)').run(voiceChannel.id, channel.kimakiDirectory, 'voice', channel.kimakiApp || null);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Show the ready message with channel links.
|
|
314
|
+
* Called at the end of startup to display available channels.
|
|
315
|
+
*/
|
|
316
|
+
function showReadyMessage({ kimakiChannels, createdChannels, appId, }) {
|
|
317
|
+
const allChannels = [];
|
|
318
|
+
allChannels.push(...createdChannels);
|
|
319
|
+
kimakiChannels.forEach(({ guild, channels }) => {
|
|
320
|
+
channels.forEach((ch) => {
|
|
321
|
+
allChannels.push({
|
|
322
|
+
name: ch.name,
|
|
323
|
+
id: ch.id,
|
|
324
|
+
guildId: guild.id,
|
|
325
|
+
directory: ch.kimakiDirectory,
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
if (allChannels.length > 0) {
|
|
330
|
+
const channelLinks = allChannels
|
|
331
|
+
.map((ch) => `• #${ch.name}: https://discord.com/channels/${ch.guildId}/${ch.id}`)
|
|
332
|
+
.join('\n');
|
|
333
|
+
note(`Your kimaki channels are ready! Click any link below to open in Discord:\n\n${channelLinks}\n\nSend a message in any channel to start using OpenCode!`, '🚀 Ready to Use');
|
|
334
|
+
}
|
|
335
|
+
note('Leave this process running to keep the bot active.\n\nIf you close this process or restart your machine, run `npx kimaki` again to start the bot.', '⚠️ Keep Running');
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Background initialization for quick start mode.
|
|
339
|
+
* Starts OpenCode server and registers slash commands without blocking bot startup.
|
|
340
|
+
*/
|
|
341
|
+
async function backgroundInit({ currentDir, token, appId, }) {
|
|
342
|
+
try {
|
|
343
|
+
const opencodeResult = await initializeOpencodeForDirectory(currentDir);
|
|
344
|
+
if (opencodeResult instanceof Error) {
|
|
345
|
+
cliLogger.warn('Background OpenCode init failed:', opencodeResult.message);
|
|
346
|
+
// Still try to register basic commands without user commands/agents
|
|
347
|
+
await registerCommands({ token, appId, userCommands: [], agents: [] });
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const getClient = opencodeResult;
|
|
351
|
+
const [userCommands, agents] = await Promise.all([
|
|
352
|
+
getClient()
|
|
353
|
+
.command.list({ query: { directory: currentDir } })
|
|
354
|
+
.then((r) => r.data || [])
|
|
355
|
+
.catch(() => []),
|
|
356
|
+
getClient()
|
|
357
|
+
.app.agents({ query: { directory: currentDir } })
|
|
358
|
+
.then((r) => r.data || [])
|
|
359
|
+
.catch(() => []),
|
|
360
|
+
]);
|
|
361
|
+
await registerCommands({ token, appId, userCommands, agents });
|
|
362
|
+
cliLogger.log('Slash commands registered!');
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
cliLogger.error('Background init failed:', error instanceof Error ? error.message : String(error));
|
|
366
|
+
}
|
|
367
|
+
}
|
|
295
368
|
async function run({ restart, addChannels }) {
|
|
296
369
|
const forceSetup = Boolean(restart);
|
|
297
370
|
intro('🤖 Discord Bot Setup');
|
|
@@ -512,17 +585,8 @@ async function run({ restart, addChannels }) {
|
|
|
512
585
|
process.exit(EXIT_NO_RESTART);
|
|
513
586
|
}
|
|
514
587
|
db.prepare('INSERT OR REPLACE INTO bot_tokens (app_id, token) VALUES (?, ?)').run(appId, token);
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (channel.kimakiDirectory) {
|
|
518
|
-
db.prepare('INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)').run(channel.id, channel.kimakiDirectory, 'text', channel.kimakiApp || null);
|
|
519
|
-
const voiceChannel = guild.channels.cache.find((ch) => ch.type === ChannelType.GuildVoice && ch.name === channel.name);
|
|
520
|
-
if (voiceChannel) {
|
|
521
|
-
db.prepare('INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)').run(voiceChannel.id, channel.kimakiDirectory, 'voice', channel.kimakiApp || null);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
588
|
+
// Store channel-directory mappings
|
|
589
|
+
storeChannelDirectories({ kimakiChannels, db });
|
|
526
590
|
if (kimakiChannels.length > 0) {
|
|
527
591
|
const channelList = kimakiChannels
|
|
528
592
|
.flatMap(({ guild, channels }) => channels.map((ch) => {
|
|
@@ -532,6 +596,19 @@ async function run({ restart, addChannels }) {
|
|
|
532
596
|
.join('\n');
|
|
533
597
|
note(channelList, 'Existing Kimaki Channels');
|
|
534
598
|
}
|
|
599
|
+
// Quick start: if setup is already done, start bot immediately and background the rest
|
|
600
|
+
const isQuickStart = existingBot && !forceSetup && !addChannels;
|
|
601
|
+
if (isQuickStart) {
|
|
602
|
+
s.start('Starting Discord bot...');
|
|
603
|
+
await startDiscordBot({ token, appId, discordClient });
|
|
604
|
+
s.stop('Discord bot is running!');
|
|
605
|
+
// Background: OpenCode init + slash command registration (non-blocking)
|
|
606
|
+
void backgroundInit({ currentDir, token, appId });
|
|
607
|
+
showReadyMessage({ kimakiChannels, createdChannels, appId });
|
|
608
|
+
outro('✨ Bot ready! Listening for messages...');
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
// Full setup path: wait for OpenCode, show prompts, create channels if needed
|
|
535
612
|
// Await the OpenCode server that was started in parallel with Discord login
|
|
536
613
|
s.start('Waiting for OpenCode server...');
|
|
537
614
|
const getClient = await opencodePromise;
|
|
@@ -656,25 +733,7 @@ async function run({ restart, addChannels }) {
|
|
|
656
733
|
s.start('Starting Discord bot...');
|
|
657
734
|
await startDiscordBot({ token, appId, discordClient });
|
|
658
735
|
s.stop('Discord bot is running!');
|
|
659
|
-
|
|
660
|
-
allChannels.push(...createdChannels);
|
|
661
|
-
kimakiChannels.forEach(({ guild, channels }) => {
|
|
662
|
-
channels.forEach((ch) => {
|
|
663
|
-
allChannels.push({
|
|
664
|
-
name: ch.name,
|
|
665
|
-
id: ch.id,
|
|
666
|
-
guildId: guild.id,
|
|
667
|
-
directory: ch.kimakiDirectory,
|
|
668
|
-
});
|
|
669
|
-
});
|
|
670
|
-
});
|
|
671
|
-
if (allChannels.length > 0) {
|
|
672
|
-
const channelLinks = allChannels
|
|
673
|
-
.map((ch) => `• #${ch.name}: https://discord.com/channels/${ch.guildId}/${ch.id}`)
|
|
674
|
-
.join('\n');
|
|
675
|
-
note(`Your kimaki channels are ready! Click any link below to open in Discord:\n\n${channelLinks}\n\nSend a message in any channel to start using OpenCode!`, '🚀 Ready to Use');
|
|
676
|
-
}
|
|
677
|
-
note('Leave this process running to keep the bot active.\n\nIf you close this process or restart your machine, run `npx kimaki` again to start the bot.', '⚠️ Keep Running');
|
|
736
|
+
showReadyMessage({ kimakiChannels, createdChannels, appId });
|
|
678
737
|
outro('✨ Setup complete! Listening for new messages... do not close this process.');
|
|
679
738
|
}
|
|
680
739
|
cli
|
package/package.json
CHANGED
|
@@ -2,18 +2,7 @@
|
|
|
2
2
|
"name": "kimaki",
|
|
3
3
|
"module": "index.ts",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.4.
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "tsx --env-file .env src/cli.ts",
|
|
8
|
-
"prepublishOnly": "pnpm tsc",
|
|
9
|
-
"dev:bun": "DEBUG=1 bun --env-file .env src/cli.ts",
|
|
10
|
-
"watch": "tsx scripts/watch-session.ts",
|
|
11
|
-
"test:events": "tsx test-events.ts",
|
|
12
|
-
"pcm-to-mp3": "bun scripts/pcm-to-mp3",
|
|
13
|
-
"test:send": "tsx send-test-message.ts",
|
|
14
|
-
"register-commands": "tsx scripts/register-commands.ts",
|
|
15
|
-
"format": "oxfmt src"
|
|
16
|
-
},
|
|
5
|
+
"version": "0.4.42",
|
|
17
6
|
"repository": "https://github.com/remorses/kimaki",
|
|
18
7
|
"bin": "bin.js",
|
|
19
8
|
"files": [
|
|
@@ -41,7 +30,6 @@
|
|
|
41
30
|
"cac": "^6.7.14",
|
|
42
31
|
"discord.js": "^14.16.3",
|
|
43
32
|
"domhandler": "^5.0.3",
|
|
44
|
-
"errore": "workspace:^",
|
|
45
33
|
"glob": "^13.0.0",
|
|
46
34
|
"htmlparser2": "^10.0.0",
|
|
47
35
|
"js-yaml": "^4.1.0",
|
|
@@ -52,10 +40,21 @@
|
|
|
52
40
|
"ripgrep-js": "^3.0.0",
|
|
53
41
|
"string-dedent": "^3.0.2",
|
|
54
42
|
"undici": "^7.16.0",
|
|
55
|
-
"zod": "^4.2.1"
|
|
43
|
+
"zod": "^4.2.1",
|
|
44
|
+
"errore": "^0.8.0"
|
|
56
45
|
},
|
|
57
46
|
"optionalDependencies": {
|
|
58
47
|
"@discordjs/opus": "^0.10.0",
|
|
59
48
|
"prism-media": "^1.3.5"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"dev": "tsx --env-file .env src/cli.ts",
|
|
52
|
+
"dev:bun": "DEBUG=1 bun --env-file .env src/cli.ts",
|
|
53
|
+
"watch": "tsx scripts/watch-session.ts",
|
|
54
|
+
"test:events": "tsx test-events.ts",
|
|
55
|
+
"pcm-to-mp3": "bun scripts/pcm-to-mp3",
|
|
56
|
+
"test:send": "tsx send-test-message.ts",
|
|
57
|
+
"register-commands": "tsx scripts/register-commands.ts",
|
|
58
|
+
"format": "oxfmt src"
|
|
60
59
|
}
|
|
61
|
-
}
|
|
60
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -389,6 +389,133 @@ async function registerCommands({
|
|
|
389
389
|
}
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
+
/**
|
|
393
|
+
* Store channel-directory mappings in the database.
|
|
394
|
+
* Called after Discord login to persist channel configurations.
|
|
395
|
+
*/
|
|
396
|
+
function storeChannelDirectories({
|
|
397
|
+
kimakiChannels,
|
|
398
|
+
db,
|
|
399
|
+
}: {
|
|
400
|
+
kimakiChannels: { guild: Guild; channels: ChannelWithTags[] }[]
|
|
401
|
+
db: ReturnType<typeof getDatabase>
|
|
402
|
+
}): void {
|
|
403
|
+
for (const { guild, channels } of kimakiChannels) {
|
|
404
|
+
for (const channel of channels) {
|
|
405
|
+
if (channel.kimakiDirectory) {
|
|
406
|
+
db.prepare(
|
|
407
|
+
'INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)',
|
|
408
|
+
).run(channel.id, channel.kimakiDirectory, 'text', channel.kimakiApp || null)
|
|
409
|
+
|
|
410
|
+
const voiceChannel = guild.channels.cache.find(
|
|
411
|
+
(ch) => ch.type === ChannelType.GuildVoice && ch.name === channel.name,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
if (voiceChannel) {
|
|
415
|
+
db.prepare(
|
|
416
|
+
'INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)',
|
|
417
|
+
).run(voiceChannel.id, channel.kimakiDirectory, 'voice', channel.kimakiApp || null)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Show the ready message with channel links.
|
|
426
|
+
* Called at the end of startup to display available channels.
|
|
427
|
+
*/
|
|
428
|
+
function showReadyMessage({
|
|
429
|
+
kimakiChannels,
|
|
430
|
+
createdChannels,
|
|
431
|
+
appId,
|
|
432
|
+
}: {
|
|
433
|
+
kimakiChannels: { guild: Guild; channels: ChannelWithTags[] }[]
|
|
434
|
+
createdChannels: { name: string; id: string; guildId: string }[]
|
|
435
|
+
appId: string
|
|
436
|
+
}): void {
|
|
437
|
+
const allChannels: {
|
|
438
|
+
name: string
|
|
439
|
+
id: string
|
|
440
|
+
guildId: string
|
|
441
|
+
directory?: string
|
|
442
|
+
}[] = []
|
|
443
|
+
|
|
444
|
+
allChannels.push(...createdChannels)
|
|
445
|
+
|
|
446
|
+
kimakiChannels.forEach(({ guild, channels }) => {
|
|
447
|
+
channels.forEach((ch) => {
|
|
448
|
+
allChannels.push({
|
|
449
|
+
name: ch.name,
|
|
450
|
+
id: ch.id,
|
|
451
|
+
guildId: guild.id,
|
|
452
|
+
directory: ch.kimakiDirectory,
|
|
453
|
+
})
|
|
454
|
+
})
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
if (allChannels.length > 0) {
|
|
458
|
+
const channelLinks = allChannels
|
|
459
|
+
.map((ch) => `• #${ch.name}: https://discord.com/channels/${ch.guildId}/${ch.id}`)
|
|
460
|
+
.join('\n')
|
|
461
|
+
|
|
462
|
+
note(
|
|
463
|
+
`Your kimaki channels are ready! Click any link below to open in Discord:\n\n${channelLinks}\n\nSend a message in any channel to start using OpenCode!`,
|
|
464
|
+
'🚀 Ready to Use',
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
note(
|
|
469
|
+
'Leave this process running to keep the bot active.\n\nIf you close this process or restart your machine, run `npx kimaki` again to start the bot.',
|
|
470
|
+
'⚠️ Keep Running',
|
|
471
|
+
)
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Background initialization for quick start mode.
|
|
476
|
+
* Starts OpenCode server and registers slash commands without blocking bot startup.
|
|
477
|
+
*/
|
|
478
|
+
async function backgroundInit({
|
|
479
|
+
currentDir,
|
|
480
|
+
token,
|
|
481
|
+
appId,
|
|
482
|
+
}: {
|
|
483
|
+
currentDir: string
|
|
484
|
+
token: string
|
|
485
|
+
appId: string
|
|
486
|
+
}): Promise<void> {
|
|
487
|
+
try {
|
|
488
|
+
const opencodeResult = await initializeOpencodeForDirectory(currentDir)
|
|
489
|
+
if (opencodeResult instanceof Error) {
|
|
490
|
+
cliLogger.warn('Background OpenCode init failed:', opencodeResult.message)
|
|
491
|
+
// Still try to register basic commands without user commands/agents
|
|
492
|
+
await registerCommands({ token, appId, userCommands: [], agents: [] })
|
|
493
|
+
return
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const getClient = opencodeResult
|
|
497
|
+
|
|
498
|
+
const [userCommands, agents] = await Promise.all([
|
|
499
|
+
getClient()
|
|
500
|
+
.command.list({ query: { directory: currentDir } })
|
|
501
|
+
.then((r) => r.data || [])
|
|
502
|
+
.catch(() => []),
|
|
503
|
+
getClient()
|
|
504
|
+
.app.agents({ query: { directory: currentDir } })
|
|
505
|
+
.then((r) => r.data || [])
|
|
506
|
+
.catch(() => []),
|
|
507
|
+
])
|
|
508
|
+
|
|
509
|
+
await registerCommands({ token, appId, userCommands, agents })
|
|
510
|
+
cliLogger.log('Slash commands registered!')
|
|
511
|
+
} catch (error) {
|
|
512
|
+
cliLogger.error(
|
|
513
|
+
'Background init failed:',
|
|
514
|
+
error instanceof Error ? error.message : String(error),
|
|
515
|
+
)
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
392
519
|
async function run({ restart, addChannels }: CliOptions) {
|
|
393
520
|
const forceSetup = Boolean(restart)
|
|
394
521
|
|
|
@@ -679,25 +806,8 @@ async function run({ restart, addChannels }: CliOptions) {
|
|
|
679
806
|
}
|
|
680
807
|
db.prepare('INSERT OR REPLACE INTO bot_tokens (app_id, token) VALUES (?, ?)').run(appId, token)
|
|
681
808
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
if (channel.kimakiDirectory) {
|
|
685
|
-
db.prepare(
|
|
686
|
-
'INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)',
|
|
687
|
-
).run(channel.id, channel.kimakiDirectory, 'text', channel.kimakiApp || null)
|
|
688
|
-
|
|
689
|
-
const voiceChannel = guild.channels.cache.find(
|
|
690
|
-
(ch) => ch.type === ChannelType.GuildVoice && ch.name === channel.name,
|
|
691
|
-
)
|
|
692
|
-
|
|
693
|
-
if (voiceChannel) {
|
|
694
|
-
db.prepare(
|
|
695
|
-
'INSERT OR IGNORE INTO channel_directories (channel_id, directory, channel_type, app_id) VALUES (?, ?, ?, ?)',
|
|
696
|
-
).run(voiceChannel.id, channel.kimakiDirectory, 'voice', channel.kimakiApp || null)
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}
|
|
809
|
+
// Store channel-directory mappings
|
|
810
|
+
storeChannelDirectories({ kimakiChannels, db })
|
|
701
811
|
|
|
702
812
|
if (kimakiChannels.length > 0) {
|
|
703
813
|
const channelList = kimakiChannels
|
|
@@ -713,6 +823,22 @@ async function run({ restart, addChannels }: CliOptions) {
|
|
|
713
823
|
note(channelList, 'Existing Kimaki Channels')
|
|
714
824
|
}
|
|
715
825
|
|
|
826
|
+
// Quick start: if setup is already done, start bot immediately and background the rest
|
|
827
|
+
const isQuickStart = existingBot && !forceSetup && !addChannels
|
|
828
|
+
if (isQuickStart) {
|
|
829
|
+
s.start('Starting Discord bot...')
|
|
830
|
+
await startDiscordBot({ token, appId, discordClient })
|
|
831
|
+
s.stop('Discord bot is running!')
|
|
832
|
+
|
|
833
|
+
// Background: OpenCode init + slash command registration (non-blocking)
|
|
834
|
+
void backgroundInit({ currentDir, token, appId })
|
|
835
|
+
|
|
836
|
+
showReadyMessage({ kimakiChannels, createdChannels, appId })
|
|
837
|
+
outro('✨ Bot ready! Listening for messages...')
|
|
838
|
+
return
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Full setup path: wait for OpenCode, show prompts, create channels if needed
|
|
716
842
|
// Await the OpenCode server that was started in parallel with Discord login
|
|
717
843
|
s.start('Waiting for OpenCode server...')
|
|
718
844
|
const getClient = await opencodePromise
|
|
@@ -875,42 +1001,7 @@ async function run({ restart, addChannels }: CliOptions) {
|
|
|
875
1001
|
await startDiscordBot({ token, appId, discordClient })
|
|
876
1002
|
s.stop('Discord bot is running!')
|
|
877
1003
|
|
|
878
|
-
|
|
879
|
-
name: string
|
|
880
|
-
id: string
|
|
881
|
-
guildId: string
|
|
882
|
-
directory?: string
|
|
883
|
-
}[] = []
|
|
884
|
-
|
|
885
|
-
allChannels.push(...createdChannels)
|
|
886
|
-
|
|
887
|
-
kimakiChannels.forEach(({ guild, channels }) => {
|
|
888
|
-
channels.forEach((ch) => {
|
|
889
|
-
allChannels.push({
|
|
890
|
-
name: ch.name,
|
|
891
|
-
id: ch.id,
|
|
892
|
-
guildId: guild.id,
|
|
893
|
-
directory: ch.kimakiDirectory,
|
|
894
|
-
})
|
|
895
|
-
})
|
|
896
|
-
})
|
|
897
|
-
|
|
898
|
-
if (allChannels.length > 0) {
|
|
899
|
-
const channelLinks = allChannels
|
|
900
|
-
.map((ch) => `• #${ch.name}: https://discord.com/channels/${ch.guildId}/${ch.id}`)
|
|
901
|
-
.join('\n')
|
|
902
|
-
|
|
903
|
-
note(
|
|
904
|
-
`Your kimaki channels are ready! Click any link below to open in Discord:\n\n${channelLinks}\n\nSend a message in any channel to start using OpenCode!`,
|
|
905
|
-
'🚀 Ready to Use',
|
|
906
|
-
)
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
note(
|
|
910
|
-
'Leave this process running to keep the bot active.\n\nIf you close this process or restart your machine, run `npx kimaki` again to start the bot.',
|
|
911
|
-
'⚠️ Keep Running',
|
|
912
|
-
)
|
|
913
|
-
|
|
1004
|
+
showReadyMessage({ kimakiChannels, createdChannels, appId })
|
|
914
1005
|
outro('✨ Setup complete! Listening for new messages... do not close this process.')
|
|
915
1006
|
}
|
|
916
1007
|
|