@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
|
|
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
|
|
653
|
+
dataDir,
|
|
653
654
|
settingsPath: options.settingsPath
|
|
654
655
|
});
|
|
655
656
|
const queueStore =
|
|
656
657
|
options.queueStore ||
|
|
657
658
|
createSpeechQueueStore({
|
|
658
|
-
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 ||
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|