shadok-ai 0.16.29 → 0.16.31
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/README.md +11 -1
- package/dist/config.d.ts +16 -0
- package/dist/config.js +16 -0
- package/dist/config.js.map +1 -1
- package/dist/server.js +56 -1
- package/dist/server.js.map +1 -1
- package/dist/whisper.d.ts +8 -0
- package/dist/whisper.js +24 -0
- package/dist/whisper.js.map +1 -1
- package/package.json +1 -1
- package/public/index.html +204 -2
package/README.md
CHANGED
|
@@ -177,7 +177,10 @@ Enter to skip; you can add it later from the web UI).
|
|
|
177
177
|
message can force through).
|
|
178
178
|
- **Notifications** — favicon, title badge and an optional sound when an agent
|
|
179
179
|
needs you. It only blinks when the tab is hidden *and* an unmuted channel is
|
|
180
|
-
actually waiting.
|
|
180
|
+
actually waiting. Mute a single agent from its menu, or **mute the whole
|
|
181
|
+
instance** from the version menu (*Mute this instance*): the favicon never
|
|
182
|
+
changes, no badge, no blink, no sound — for every agent and every tab, saved
|
|
183
|
+
per instance like the cockpit's name and palette.
|
|
181
184
|
- **Engine room** — the raw TUI screen, live, with clickable keys, for anything
|
|
182
185
|
the chat can't express. With tmux there is also an **experimental real
|
|
183
186
|
terminal** (xterm.js over the pane's byte stream) when a snapshot isn't enough.
|
|
@@ -191,6 +194,13 @@ Enter to skip; you can add it later from the web UI).
|
|
|
191
194
|
`SHADOK_WHISPER_MODEL` picks a bigger one if your recordings need it.
|
|
192
195
|
Set `SHADOK_WHISPER_DIR` to a directory shared between containers and one copy
|
|
193
196
|
serves them all.
|
|
197
|
+
- **Dictate a prompt on the web** — a 🎙 button beside *Send* records your
|
|
198
|
+
microphone and transcribes it with the **same local whisper.cpp** the voice
|
|
199
|
+
notes use (nothing leaves the machine). The text drops into the composer
|
|
200
|
+
**editable**, never auto-sent, so a misheard word is fixed before it goes —
|
|
201
|
+
click to start, click to stop, a red timer runs while recording. Needs a
|
|
202
|
+
secure context (localhost or HTTPS) for the browser to grant the mic; the
|
|
203
|
+
first use pays the same one-time whisper install as the voice notes above.
|
|
194
204
|
- **Videos on Telegram** — share a video (or a GIF, or a round video note) and
|
|
195
205
|
the agent actually sees it: shadok pulls a handful of stills out of the clip
|
|
196
206
|
for it to look at, and transcribes the sound when there is speech. Both run
|
package/dist/config.d.ts
CHANGED
|
@@ -68,6 +68,15 @@ export interface Config {
|
|
|
68
68
|
* this instance and survives a reload.
|
|
69
69
|
*/
|
|
70
70
|
cockpitTheme?: Record<string, string>;
|
|
71
|
+
/**
|
|
72
|
+
* Per launch-directory "mute the whole instance", keyed by absolute cwd.
|
|
73
|
+
* `true` = every agent on this instance raises NO web attention signal: the
|
|
74
|
+
* favicon never changes, no title badge, no blink, no chime. Absent/false =
|
|
75
|
+
* the normal per-agent behaviour. It is the instance-wide twin of the
|
|
76
|
+
* per-channel `Channel.muted`, shared across every tab and device like the
|
|
77
|
+
* name and palette.
|
|
78
|
+
*/
|
|
79
|
+
cockpitMuted?: Record<string, boolean>;
|
|
71
80
|
/**
|
|
72
81
|
* Per launch-directory two-factor sign-in (a passkey after the password),
|
|
73
82
|
* keyed by absolute cwd. Absent = off. Turned on from the Users panel, under
|
|
@@ -112,6 +121,13 @@ export declare function themeForCwd(cfg: Config, cwd: string): string | null;
|
|
|
112
121
|
* any unknown key clear the entry, so the instance falls back to the default.
|
|
113
122
|
*/
|
|
114
123
|
export declare function setThemeForCwd(cfg: Config, cwd: string, theme: string): void;
|
|
124
|
+
/** Is this whole instance muted (no web attention signal for any agent)? */
|
|
125
|
+
export declare function mutedForCwd(cfg: Config, cwd: string): boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Set (or clear) this directory's instance-wide mute and persist. `false`
|
|
128
|
+
* removes the entry — the default is "not muted", stored as absent.
|
|
129
|
+
*/
|
|
130
|
+
export declare function setMutedForCwd(cfg: Config, cwd: string, on: boolean): void;
|
|
115
131
|
/** The model backend chosen for the instance launched in `cwd`, or null. */
|
|
116
132
|
export declare function backendForCwd(cfg: Config, cwd: string): ConfiguredBackend | null;
|
|
117
133
|
/** Set (or clear, with null) that backend and persist. */
|
package/dist/config.js
CHANGED
|
@@ -74,6 +74,22 @@ export function setThemeForCwd(cfg, cwd, theme) {
|
|
|
74
74
|
delete cfg.cockpitTheme[cwd];
|
|
75
75
|
saveConfig(cfg);
|
|
76
76
|
}
|
|
77
|
+
/** Is this whole instance muted (no web attention signal for any agent)? */
|
|
78
|
+
export function mutedForCwd(cfg, cwd) {
|
|
79
|
+
return cfg.cockpitMuted?.[cwd] === true;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Set (or clear) this directory's instance-wide mute and persist. `false`
|
|
83
|
+
* removes the entry — the default is "not muted", stored as absent.
|
|
84
|
+
*/
|
|
85
|
+
export function setMutedForCwd(cfg, cwd, on) {
|
|
86
|
+
cfg.cockpitMuted ??= {};
|
|
87
|
+
if (on)
|
|
88
|
+
cfg.cockpitMuted[cwd] = true;
|
|
89
|
+
else
|
|
90
|
+
delete cfg.cockpitMuted[cwd];
|
|
91
|
+
saveConfig(cfg);
|
|
92
|
+
}
|
|
77
93
|
/** The model backend chosen for the instance launched in `cwd`, or null. */
|
|
78
94
|
export function backendForCwd(cfg, cwd) {
|
|
79
95
|
return cfg.modelBackend?.[cwd] ?? null;
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAkG7B,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AAChE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AACzD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;AAEzD,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;QAC3D,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7E,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,qCAAqC;AACzE,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,GAAW;IAClD,OAAO,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,GAAW,EAAE,KAAoB;IAC3E,CAAC,GAAG,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACjC,UAAU,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,GAAW,EAAE,GAAG,GAAsB,OAAO,CAAC,GAAG;IAC3F,IAAI,GAAG,CAAC,kBAAkB;QAAE,OAAO,GAAG,CAAC,kBAAkB,CAAC;IAC1D,OAAO,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AACnC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,GAAW;IAClD,MAAM,CAAC,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,GAAW,EAAE,KAAa;IACpE,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACvB,GAAG,CAAC,YAAY,KAAK,EAAE,CAAC;IACxB,IAAI,CAAC;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;;QAC5B,OAAO,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAClC,UAAU,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAU,CAAC;AAE/F,uEAAuE;AACvE,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,GAAW;IAClD,MAAM,CAAC,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,IAAK,cAAoC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5F,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,GAAW,EAAE,KAAa;IACpE,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/B,GAAG,CAAC,YAAY,KAAK,EAAE,CAAC;IACxB,IAAI,CAAC,IAAI,CAAC,KAAK,OAAO,IAAK,cAAoC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;;QAClG,OAAO,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAClC,UAAU,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,GAAW;IAClD,OAAO,GAAG,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,GAAW,EAAE,EAAW;IAClE,GAAG,CAAC,YAAY,KAAK,EAAE,CAAC;IACxB,IAAI,EAAE;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;;QAChC,OAAO,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAClC,UAAU,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,GAAW;IACpD,OAAO,GAAG,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AACzC,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,gBAAgB,CAAC,GAAW,EAAE,GAAW,EAAE,CAA2B;IACpF,GAAG,CAAC,YAAY,KAAK,EAAE,CAAC;IACxB,IAAI,CAAC;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;;QAC5B,OAAO,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAClC,UAAU,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,GAAW;IACtD,OAAO,GAAG,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,GAAW,EAAE,EAAW;IACtE,GAAG,CAAC,SAAS,KAAK,EAAE,CAAC;IACrB,IAAI,EAAE;QAAE,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;;QAC7B,OAAO,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC/B,UAAU,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC;AASD,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,GAAW,EAAE,GAAG,GAAsB,OAAO,CAAC,GAAG;IAC3F,MAAM,MAAM,GAAG,GAAG,CAAC,kBAAkB,CAAC;IACtC,MAAM,KAAK,GAAG,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IAClD,MAAM,QAAQ,GAAG,GAAG,CAAC,sBAAsB,CAAC;IAC5C,MAAM,YAAY,GAAG,QAAQ;QAC3B,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QAC1D,CAAC,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IACrC,OAAO;QACL,KAAK;QACL,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC;QAC5B,OAAO,EAAE,GAAG,CAAC,eAAe,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI;QAC3C,YAAY;KACb,CAAC;AACJ,CAAC;AAQD;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,GAAW,EAAE,KAAoB,EAAE,WAAoB;IACrG,MAAM,IAAI,GAAW;QACnB,GAAG,GAAG;QACN,MAAM,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE;QACzB,eAAe,EAAE,EAAE,GAAG,GAAG,CAAC,eAAe,EAAE;QAC3C,eAAe,EAAE,EAAE,GAAG,GAAG,CAAC,eAAe,EAAE;KAC5C,CAAC;IACF,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,WAAW,EAAE,CAAC;QAC9C,IAAI,CAAC,MAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACvD,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,IAAI,CAAC,eAAgB,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;IAC5E,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;QAAE,IAAI,CAAC,eAAgB,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC;IACtF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAChE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACzB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,GAAW,EAAE,WAAoB;IAC/E,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,CAAC,WAAW;QAAE,OAAO;IAC5D,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,IAAI,CAAC;QACH,KAAK,GAAG,gBAAgB,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,iBAAiB;IACnB,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ;QAAE,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC;IAC/E,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,CAAC,GAAG,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACjC,OAAO,GAAG,CAAC,aAAa,CAAC,CAAC,2BAA2B;IACrD,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,0BAA0B;IACjF,CAAC;IAAC,MAAM,CAAC;QACP,iBAAiB;IACnB,CAAC;IACD,UAAU,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC"}
|
package/dist/server.js
CHANGED
|
@@ -39,6 +39,7 @@ import { getUsage } from "./usage.js";
|
|
|
39
39
|
import { loadChannels, loadGroups, saveGroups, upsertChannel, removeChannel, mergeClientChannels, channelWriteAllowed, profilePatch, loadTgGroup, saveTgGroup, isHomeChannel, homeAdoptionTarget, resolveSessionTarget, resolveResumeId, resumeTarget, } from "./channels.js";
|
|
40
40
|
import { deliverWithRetry } from "./reload.js";
|
|
41
41
|
import { startTelegram, renameTelegramTopic, closeTelegramTopic, probeToken, isStalePreface, announceLoggedOut, resetLoggedOutNotice, attachmentPrompt, pasteFileName, MEDIA_DIR, } from "./telegram.js";
|
|
42
|
+
import { transcribe, audioClipExtension } from "./whisper.js";
|
|
42
43
|
import { cancelPairingCode, issuePairingCode, pairingStatus } from "./tg-pairing.js";
|
|
43
44
|
import { migrateTgBindings } from "./channels.js";
|
|
44
45
|
import { instanceKey } from "./paths.js";
|
|
@@ -51,7 +52,7 @@ import { ledgerFileFor, ensureLedgerFile, loadLedger, deltaSince, formatLedgerCh
|
|
|
51
52
|
// the server imports it rather than keeping a second copy of the kind inference.
|
|
52
53
|
import { rowKind } from "../context/ledger-skill/ledger-core.mjs";
|
|
53
54
|
import { linkRefusal, markAgentPrompt, notificationText, } from "./kinship.js";
|
|
54
|
-
import { loadConfig, saveConfig, telegramConfig, applyTelegramPatch, titleForCwd, setTitleForCwd, themeForCwd, setThemeForCwd, twoFactorForCwd, setTwoFactorForCwd, backendForCwd, setBackendForCwd, } from "./config.js";
|
|
55
|
+
import { loadConfig, saveConfig, telegramConfig, applyTelegramPatch, titleForCwd, setTitleForCwd, themeForCwd, setThemeForCwd, mutedForCwd, setMutedForCwd, twoFactorForCwd, setTwoFactorForCwd, backendForCwd, setBackendForCwd, } from "./config.js";
|
|
55
56
|
import { secretsFor, secretNames, setSecret, deleteSecret, secretWriteVerdict, recordOrigin, originProfile, } from "./secrets.js";
|
|
56
57
|
import { recordSecretAudit, readSecretAudit, filterAudit, spawnActor, httpActor, profileSecretDiff, } from "./secret-audit.js";
|
|
57
58
|
import { getProfile, profileArgs, permissionModeArgs, isPermissionMode, isEffortLevel, mcpServersVerdict, envVarsNote, seedDefaultProfiles, migrateToTracking, seedTweakProfile, loadProfiles, upsertProfile, removeProfile, promptEditVerdict, secretAttachVerdict, READONLY_DENY, TWEAK_PROFILE_NAME, promptOrigin, effectiveProfile, shippedProfile, } from "./profiles.js";
|
|
@@ -1267,6 +1268,50 @@ app.post("/paste", express.raw({ type: () => true, limit: PASTE_LIMIT }), (req,
|
|
|
1267
1268
|
res.status(500).json({ error: e instanceof Error ? e.message : String(e) });
|
|
1268
1269
|
}
|
|
1269
1270
|
});
|
|
1271
|
+
/**
|
|
1272
|
+
* Speech-to-text for the composer's microphone button. The browser records a
|
|
1273
|
+
* short clip (MediaRecorder → webm/opus) and POSTs the raw bytes here; we run
|
|
1274
|
+
* the SAME local whisper.cpp pipeline the Telegram voice path uses
|
|
1275
|
+
* (`transcribe`) and return the text for the client to drop into the composer.
|
|
1276
|
+
*
|
|
1277
|
+
* The text is NEVER auto-sent — it lands in the editable composer so the user
|
|
1278
|
+
* reads it before acting, because a misheard instruction acted on in silence is
|
|
1279
|
+
* worse than a voice note that was ignored (the reason `heardNotice` exists).
|
|
1280
|
+
*
|
|
1281
|
+
* `requestFromBrowser` like `/paste`: this writes a file and records the mic, a
|
|
1282
|
+
* browser-only capability with no business being reachable by a script on
|
|
1283
|
+
* loopback. The clip is TRANSIENT — written to the OS temp dir and removed in
|
|
1284
|
+
* `finally`, never kept in `MEDIA_DIR`: it is spoken input, not a file to hand
|
|
1285
|
+
* the agent.
|
|
1286
|
+
*
|
|
1287
|
+
* The first call on a fresh instance provisions whisper (ffmpeg / model / build
|
|
1288
|
+
* via `ensureWhisper` inside `transcribe`), which can take minutes; the client
|
|
1289
|
+
* shows a working state and we simply wait. The French whisper notices are kept
|
|
1290
|
+
* server-side — the client maps `kind` to its own English UI copy, so the web
|
|
1291
|
+
* stays English while Telegram keeps its spoken-language notices.
|
|
1292
|
+
*/
|
|
1293
|
+
app.post("/transcribe", express.raw({ type: () => true, limit: PASTE_LIMIT }), async (req, res) => {
|
|
1294
|
+
if (!requestFromBrowser(req))
|
|
1295
|
+
return res.status(403).json({ error: "same-origin browser only" });
|
|
1296
|
+
const body = req.body;
|
|
1297
|
+
if (!Buffer.isBuffer(body) || !body.length)
|
|
1298
|
+
return res.status(400).json({ error: "empty body" });
|
|
1299
|
+
const ext = audioClipExtension(String(req.headers["content-type"] ?? ""));
|
|
1300
|
+
const tmp = path.join(os.tmpdir(), `shadok-stt-${randomUUID()}.${ext}`);
|
|
1301
|
+
try {
|
|
1302
|
+
await fs.promises.writeFile(tmp, body);
|
|
1303
|
+
const out = await transcribe(tmp);
|
|
1304
|
+
if (out.ok)
|
|
1305
|
+
return res.json({ ok: true, text: out.text });
|
|
1306
|
+
return res.json({ ok: false, kind: out.kind });
|
|
1307
|
+
}
|
|
1308
|
+
catch (e) {
|
|
1309
|
+
return res.status(500).json({ ok: false, error: e instanceof Error ? e.message : String(e) });
|
|
1310
|
+
}
|
|
1311
|
+
finally {
|
|
1312
|
+
await fs.promises.rm(tmp, { force: true }).catch(() => { });
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1270
1315
|
app.use(express.static(path.join(__dirname, "..", "public")));
|
|
1271
1316
|
// Markdown parser served locally (history rendering on the client).
|
|
1272
1317
|
// Resolved through require.resolve, not a hardcoded path: in the npm install
|
|
@@ -1390,6 +1435,16 @@ app.put("/theme", (req, res) => {
|
|
|
1390
1435
|
setThemeForCwd(loadConfig(), process.cwd(), String(req.body?.theme ?? ""));
|
|
1391
1436
|
res.json({ theme: themeForCwd(loadConfig(), process.cwd()) });
|
|
1392
1437
|
});
|
|
1438
|
+
// Instance-wide mute, per launch directory. `true` = NO web attention signal for
|
|
1439
|
+
// any agent (favicon frozen, no title badge, no blink, no chime). Purely a
|
|
1440
|
+
// display preference the client reads — no agent is restarted, unlike /ledger.
|
|
1441
|
+
app.get("/mute", (_req, res) => {
|
|
1442
|
+
res.json({ muted: mutedForCwd(loadConfig(), process.cwd()) });
|
|
1443
|
+
});
|
|
1444
|
+
app.put("/mute", (req, res) => {
|
|
1445
|
+
setMutedForCwd(loadConfig(), process.cwd(), req.body?.muted === true);
|
|
1446
|
+
res.json({ muted: mutedForCwd(loadConfig(), process.cwd()) });
|
|
1447
|
+
});
|
|
1393
1448
|
// Materialises shadok-ai's own source for the "Tweak Shadok-AI" CTA, and hands
|
|
1394
1449
|
// back the directory to start the agent in. Sits here so it inherits the same
|
|
1395
1450
|
// password gate as its neighbours.
|