@micsushi/agent-hotline 1.0.1 → 1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@micsushi/agent-hotline",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Local read-aloud hooks and tray app for AI coding agents.",
5
5
  "bin": {
6
6
  "ah": "packages/backend/bin/agent-hotline.js",
@@ -36,6 +36,8 @@
36
36
  "install-hotline": "node packages/backend/bin/agent-hotline.js install",
37
37
  "stage-web": "node scripts/stage-web.mjs",
38
38
  "prepack": "node scripts/stage-web.mjs",
39
+ "build:backend-sea": "node scripts/build-backend-sea.mjs",
40
+ "sync-desktop-version": "node scripts/sync-desktop-version.mjs",
39
41
  "test": "npm --prefix packages/backend test && npm --workspace @agent-hotline/desktop run test",
40
42
  "lint": "eslint . && npm run rust:clippy",
41
43
  "lint:fix": "eslint . --fix",
@@ -51,8 +53,10 @@
51
53
  },
52
54
  "devDependencies": {
53
55
  "@eslint/js": "^10.0.1",
56
+ "esbuild": "^0.28.1",
54
57
  "eslint": "^10.5.0",
55
58
  "globals": "^17.6.0",
59
+ "postject": "^1.0.0-alpha.6",
56
60
  "prettier": "^3.8.4"
57
61
  },
58
62
  "repository": {
@@ -15,7 +15,7 @@ const { createAudioCacheStore } = require("./audio-cache-store");
15
15
  const PORT = Number(process.env.AGENT_HOTLINE_PORT || process.env.VOICE_QUESTION_LOOP_PORT || 4777);
16
16
  const HOST = "127.0.0.1";
17
17
  const ROOT = path.resolve(__dirname, "..");
18
- const DATA_DIR = path.join(ROOT, "data");
18
+ const DATA_DIR = process.env.AGENT_HOTLINE_DATA_DIR || path.join(ROOT, "data");
19
19
  const QUESTIONS_FILE = process.env.QUESTION_FILE || path.join(DATA_DIR, "questions.json");
20
20
  const REQUEST_LIMIT_BYTES = 1_000_000;
21
21
  const AUDIO_BODY_LIMIT_BYTES = 96 * 1024 * 1024;
@@ -646,22 +646,23 @@ function page() {
646
646
  }
647
647
 
648
648
  function createServer(options = {}) {
649
+ const dataDir = options.dataDir || DATA_DIR;
649
650
  const settingsStore =
650
651
  options.settingsStore ||
651
652
  createSettingsStore({
652
- dataDir: options.dataDir,
653
+ dataDir,
653
654
  settingsPath: options.settingsPath
654
655
  });
655
656
  const queueStore =
656
657
  options.queueStore ||
657
658
  createSpeechQueueStore({
658
- dataDir: options.dataDir,
659
+ dataDir,
659
660
  filePath: options.queuePath
660
661
  });
661
662
  const questionStore =
662
663
  options.questionStore ||
663
664
  createQuestionStore({
664
- dataDir: options.questionDataDir || options.dataDir,
665
+ dataDir: options.questionDataDir || dataDir,
665
666
  questionsFile: options.questionsFile,
666
667
  answersFile: options.answersFile,
667
668
  ensureFiles: options.ensureQuestionFiles
@@ -670,7 +671,7 @@ function createServer(options = {}) {
670
671
  const audioCacheStore =
671
672
  options.audioCacheStore ||
672
673
  createAudioCacheStore({
673
- dataDir: options.dataDir,
674
+ dataDir,
674
675
  cacheDir: options.audioCacheDir,
675
676
  maxBytes: options.audioMaxBytes,
676
677
  getMaxBytes: options.audioMaxBytes
@@ -679,8 +680,7 @@ function createServer(options = {}) {
679
680
  });
680
681
 
681
682
  const spoolStore =
682
- options.spoolStore ||
683
- createSpoolStore({ dataDir: options.dataDir, filePath: options.spoolPath });
683
+ options.spoolStore || createSpoolStore({ dataDir, filePath: options.spoolPath });
684
684
  try {
685
685
  spoolStore.drain((item) => queueStore.enqueue(item));
686
686
  } catch {}
@@ -8,6 +8,7 @@ const READ_BEHAVIORS = new Set(["manual", "auto"]);
8
8
  const TTS_ENGINES = new Set(["webview", "kokoro", "kokoro-ts"]);
9
9
  const NOTIFICATION_OPENS = new Set(["full", "mini"]);
10
10
  const AUDIO_CACHE_LIMIT_MAX_MB = 100000;
11
+ const DEFAULT_RATE = 0.9;
11
12
 
12
13
  const DEFAULT_SETTINGS = Object.freeze({
13
14
  readBehavior: "manual",
@@ -16,7 +17,7 @@ const DEFAULT_SETTINGS = Object.freeze({
16
17
  voice: "",
17
18
  audioOutputDeviceId: "",
18
19
  kokoroVoice: "af_heart",
19
- rate: 0.92,
20
+ rate: DEFAULT_RATE,
20
21
  volume: 1,
21
22
  skipRules: Object.freeze({
22
23
  codeBlocks: true,
@@ -32,7 +33,9 @@ const DEFAULT_SETTINGS = Object.freeze({
32
33
  notifyOnNewReply: false,
33
34
  notificationOpens: "full",
34
35
  highlightSpokenText: false,
35
- audioCacheLimitMb: 1024
36
+ audioCacheLimitMb: 1024,
37
+ startupSplash: true,
38
+ startupJingle: true
36
39
  });
37
40
 
38
41
  function getDefaultDataDir(env = process.env, platform = process.platform) {
@@ -79,6 +82,11 @@ function numberInRangeOrDefault(value, fallback, min, max) {
79
82
  return number >= min && number <= max ? number : fallback;
80
83
  }
81
84
 
85
+ function normalizeRate(value, fallback) {
86
+ const rate = numberInRangeOrDefault(value, fallback, 0.1, 10);
87
+ return rate === 0.92 ? DEFAULT_RATE : rate;
88
+ }
89
+
82
90
  function normalizeSettings(input) {
83
91
  const source = isPlainObject(input) ? input : {};
84
92
  const defaults = DEFAULT_SETTINGS;
@@ -93,7 +101,7 @@ function normalizeSettings(input) {
93
101
  voice: stringOrDefault(source.voice, defaults.voice),
94
102
  audioOutputDeviceId: stringOrDefault(source.audioOutputDeviceId, defaults.audioOutputDeviceId),
95
103
  kokoroVoice: stringOrDefault(source.kokoroVoice, defaults.kokoroVoice),
96
- rate: numberInRangeOrDefault(source.rate, defaults.rate, 0.1, 10),
104
+ rate: normalizeRate(source.rate, defaults.rate),
97
105
  volume: numberInRangeOrDefault(source.volume, defaults.volume, 0, 1),
98
106
  skipRules: {
99
107
  codeBlocks: booleanOrDefault(sourceSkipRules.codeBlocks, defaults.skipRules.codeBlocks),
@@ -119,7 +127,9 @@ function normalizeSettings(input) {
119
127
  defaults.audioCacheLimitMb,
120
128
  10,
121
129
  AUDIO_CACHE_LIMIT_MAX_MB
122
- )
130
+ ),
131
+ startupSplash: booleanOrDefault(source.startupSplash, defaults.startupSplash),
132
+ startupJingle: booleanOrDefault(source.startupJingle, defaults.startupJingle)
123
133
  };
124
134
  }
125
135