kimaki 0.4.48 → 0.4.49

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 (3) hide show
  1. package/dist/cli.js +28 -10
  2. package/package.json +1 -1
  3. package/src/cli.ts +34 -10
package/dist/cli.js CHANGED
@@ -17,6 +17,15 @@ import http from 'node:http';
17
17
  import { setDataDir, getDataDir, getLockPort, setDefaultVerbosity } from './config.js';
18
18
  import { sanitizeAgentName } from './commands/agent.js';
19
19
  const cliLogger = createLogger(LogPrefix.CLI);
20
+ // Strip bracketed paste escape sequences from terminal input.
21
+ // iTerm2 and other terminals wrap pasted content with \x1b[200~ and \x1b[201~
22
+ // which can cause validation to fail on macOS. See: https://github.com/remorses/kimaki/issues/18
23
+ function stripBracketedPaste(value) {
24
+ if (!value) {
25
+ return '';
26
+ }
27
+ return value.replace(/\x1b\[200~/g, '').replace(/\x1b\[201~/g, '').trim();
28
+ }
20
29
  // Spawn caffeinate on macOS to prevent system sleep while bot is running.
21
30
  // Not detached, so it dies automatically with the parent process.
22
31
  function startCaffeinate() {
@@ -499,17 +508,20 @@ async function run({ restart, addChannels, useWorktrees }) {
499
508
  message: 'Enter your Discord Application ID:',
500
509
  placeholder: 'e.g., 1234567890123456789',
501
510
  validate(value) {
502
- if (!value)
511
+ const cleaned = stripBracketedPaste(value);
512
+ if (!cleaned) {
503
513
  return 'Application ID is required';
504
- if (!/^\d{17,20}$/.test(value))
514
+ }
515
+ if (!/^\d{17,20}$/.test(cleaned)) {
505
516
  return 'Invalid Application ID format (should be 17-20 digits)';
517
+ }
506
518
  },
507
519
  });
508
520
  if (isCancel(appIdInput)) {
509
521
  cancel('Setup cancelled');
510
522
  process.exit(0);
511
523
  }
512
- appId = appIdInput;
524
+ appId = stripBracketedPaste(appIdInput);
513
525
  note('1. Go to the "Bot" section in the left sidebar\n' +
514
526
  '2. Scroll down to "Privileged Gateway Intents"\n' +
515
527
  '3. Enable these intents by toggling them ON:\n' +
@@ -530,33 +542,39 @@ async function run({ restart, addChannels, useWorktrees }) {
530
542
  const tokenInput = await password({
531
543
  message: 'Enter your Discord Bot Token (from "Bot" section - click "Reset Token" if needed):',
532
544
  validate(value) {
533
- if (!value)
545
+ const cleaned = stripBracketedPaste(value);
546
+ if (!cleaned) {
534
547
  return 'Bot token is required';
535
- if (value.length < 50)
548
+ }
549
+ if (cleaned.length < 50) {
536
550
  return 'Invalid token format (too short)';
551
+ }
537
552
  },
538
553
  });
539
554
  if (isCancel(tokenInput)) {
540
555
  cancel('Setup cancelled');
541
556
  process.exit(0);
542
557
  }
543
- token = tokenInput;
558
+ token = stripBracketedPaste(tokenInput);
544
559
  note(`You can get a Gemini api Key at https://aistudio.google.com/apikey`, `Gemini API Key`);
545
- const geminiApiKey = await password({
560
+ const geminiApiKeyInput = await password({
546
561
  message: 'Enter your Gemini API Key for voice channels and audio transcription (optional, press Enter to skip):',
547
562
  validate(value) {
548
- if (value && value.length < 10)
563
+ const cleaned = stripBracketedPaste(value);
564
+ if (cleaned && cleaned.length < 10) {
549
565
  return 'Invalid API key format';
566
+ }
550
567
  return undefined;
551
568
  },
552
569
  });
553
- if (isCancel(geminiApiKey)) {
570
+ if (isCancel(geminiApiKeyInput)) {
554
571
  cancel('Setup cancelled');
555
572
  process.exit(0);
556
573
  }
574
+ const geminiApiKey = stripBracketedPaste(geminiApiKeyInput) || null;
557
575
  // Store API key in database
558
576
  if (geminiApiKey) {
559
- db.prepare('INSERT OR REPLACE INTO bot_api_keys (app_id, gemini_api_key) VALUES (?, ?)').run(appId, geminiApiKey || null);
577
+ db.prepare('INSERT OR REPLACE INTO bot_api_keys (app_id, gemini_api_key) VALUES (?, ?)').run(appId, geminiApiKey);
560
578
  note('API key saved successfully', 'API Key Stored');
561
579
  }
562
580
  note(`Bot install URL:\n${generateBotInstallUrl({ clientId: appId })}\n\nYou MUST install the bot in your Discord server before continuing.`, 'Step 4: Install Bot to Server');
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.48",
5
+ "version": "0.4.49",
6
6
  "repository": "https://github.com/remorses/kimaki",
7
7
  "bin": "bin.js",
8
8
  "files": [
package/src/cli.ts CHANGED
@@ -52,6 +52,16 @@ import { sanitizeAgentName } from './commands/agent.js'
52
52
 
53
53
  const cliLogger = createLogger(LogPrefix.CLI)
54
54
 
55
+ // Strip bracketed paste escape sequences from terminal input.
56
+ // iTerm2 and other terminals wrap pasted content with \x1b[200~ and \x1b[201~
57
+ // which can cause validation to fail on macOS. See: https://github.com/remorses/kimaki/issues/18
58
+ function stripBracketedPaste(value: string | undefined): string {
59
+ if (!value) {
60
+ return ''
61
+ }
62
+ return value.replace(/\x1b\[200~/g, '').replace(/\x1b\[201~/g, '').trim()
63
+ }
64
+
55
65
  // Spawn caffeinate on macOS to prevent system sleep while bot is running.
56
66
  // Not detached, so it dies automatically with the parent process.
57
67
  function startCaffeinate() {
@@ -696,9 +706,13 @@ async function run({ restart, addChannels, useWorktrees }: CliOptions) {
696
706
  message: 'Enter your Discord Application ID:',
697
707
  placeholder: 'e.g., 1234567890123456789',
698
708
  validate(value) {
699
- if (!value) return 'Application ID is required'
700
- if (!/^\d{17,20}$/.test(value))
709
+ const cleaned = stripBracketedPaste(value)
710
+ if (!cleaned) {
711
+ return 'Application ID is required'
712
+ }
713
+ if (!/^\d{17,20}$/.test(cleaned)) {
701
714
  return 'Invalid Application ID format (should be 17-20 digits)'
715
+ }
702
716
  },
703
717
  })
704
718
 
@@ -706,7 +720,7 @@ async function run({ restart, addChannels, useWorktrees }: CliOptions) {
706
720
  cancel('Setup cancelled')
707
721
  process.exit(0)
708
722
  }
709
- appId = appIdInput
723
+ appId = stripBracketedPaste(appIdInput)
710
724
 
711
725
  note(
712
726
  '1. Go to the "Bot" section in the left sidebar\n' +
@@ -737,8 +751,13 @@ async function run({ restart, addChannels, useWorktrees }: CliOptions) {
737
751
  const tokenInput = await password({
738
752
  message: 'Enter your Discord Bot Token (from "Bot" section - click "Reset Token" if needed):',
739
753
  validate(value) {
740
- if (!value) return 'Bot token is required'
741
- if (value.length < 50) return 'Invalid token format (too short)'
754
+ const cleaned = stripBracketedPaste(value)
755
+ if (!cleaned) {
756
+ return 'Bot token is required'
757
+ }
758
+ if (cleaned.length < 50) {
759
+ return 'Invalid token format (too short)'
760
+ }
742
761
  },
743
762
  })
744
763
 
@@ -746,29 +765,34 @@ async function run({ restart, addChannels, useWorktrees }: CliOptions) {
746
765
  cancel('Setup cancelled')
747
766
  process.exit(0)
748
767
  }
749
- token = tokenInput
768
+ token = stripBracketedPaste(tokenInput)
750
769
 
751
770
  note(`You can get a Gemini api Key at https://aistudio.google.com/apikey`, `Gemini API Key`)
752
771
 
753
- const geminiApiKey = await password({
772
+ const geminiApiKeyInput = await password({
754
773
  message:
755
774
  'Enter your Gemini API Key for voice channels and audio transcription (optional, press Enter to skip):',
756
775
  validate(value) {
757
- if (value && value.length < 10) return 'Invalid API key format'
776
+ const cleaned = stripBracketedPaste(value)
777
+ if (cleaned && cleaned.length < 10) {
778
+ return 'Invalid API key format'
779
+ }
758
780
  return undefined
759
781
  },
760
782
  })
761
783
 
762
- if (isCancel(geminiApiKey)) {
784
+ if (isCancel(geminiApiKeyInput)) {
763
785
  cancel('Setup cancelled')
764
786
  process.exit(0)
765
787
  }
766
788
 
789
+ const geminiApiKey = stripBracketedPaste(geminiApiKeyInput) || null
790
+
767
791
  // Store API key in database
768
792
  if (geminiApiKey) {
769
793
  db.prepare('INSERT OR REPLACE INTO bot_api_keys (app_id, gemini_api_key) VALUES (?, ?)').run(
770
794
  appId,
771
- geminiApiKey || null,
795
+ geminiApiKey,
772
796
  )
773
797
  note('API key saved successfully', 'API Key Stored')
774
798
  }