@iaforged/context-code 1.1.4 → 1.1.7
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 +32 -8
- package/dist/src/commands/init.js +91 -219
- package/dist/src/commands/voice/index.js +6 -7
- package/dist/src/commands/voice/voice.js +87 -43
- package/dist/src/commands.js +1 -3
- package/dist/src/components/LogoV2/VoiceModeNotice.js +1 -1
- package/dist/src/components/PromptInput/VoiceIndicator.js +4 -4
- package/dist/src/components/Spinner.js +18 -18
- package/dist/src/constants/spinnerVerbs.js +9 -9
- package/dist/src/hooks/usePasteHandler.js +8 -8
- package/dist/src/hooks/useVoice.js +93 -805
- package/dist/src/hooks/useVoiceEnabled.js +3 -15
- package/dist/src/hooks/useVoiceIntegration.js +6 -25
- package/dist/src/keybindings/defaultBindings.js +9 -6
- package/dist/src/screens/REPL.js +10 -22
- package/dist/src/services/localDictation.js +520 -0
- package/dist/src/services/voice.js +9 -7
- package/dist/src/state/AppState.js +1 -3
- package/dist/src/tools/ConfigTool/ConfigTool.js +12 -15
- package/dist/src/tools/ConfigTool/supportedSettings.js +2 -2
- package/dist/src/utils/imagePaste.js +11 -5
- package/dist/src/utils/model/model.js +2 -0
- package/dist/src/utils/settings/types.js +2 -2
- package/dist/src/voice/voiceModeEnabled.js +5 -25
- package/dist/vendor/audio-capture/arm64-darwin/audio-capture.node +0 -0
- package/dist/vendor/audio-capture/arm64-linux/audio-capture.node +0 -0
- package/dist/vendor/audio-capture/arm64-win32/audio-capture.node +0 -0
- package/dist/vendor/audio-capture/x64-darwin/audio-capture.node +0 -0
- package/dist/vendor/audio-capture/x64-linux/audio-capture.node +0 -0
- package/dist/vendor/audio-capture/x64-win32/audio-capture.node +0 -0
- package/dist/vendor/audio-capture-src/index.js +114 -0
- package/dist/vendor/audio-capture-src/index.ts +155 -0
- package/docs/comandos.md +132 -121
- package/package.json +1 -1
|
@@ -99,7 +99,7 @@ export const SUPPORTED_SETTINGS = {
|
|
|
99
99
|
language: {
|
|
100
100
|
source: 'settings',
|
|
101
101
|
type: 'string',
|
|
102
|
-
description: '
|
|
102
|
+
description: 'Idioma preferido para las respuestas de Context y el dictado local (por ejemplo, "japanese", "spanish")',
|
|
103
103
|
},
|
|
104
104
|
teammateMode: {
|
|
105
105
|
source: 'global',
|
|
@@ -121,7 +121,7 @@ export const SUPPORTED_SETTINGS = {
|
|
|
121
121
|
voiceEnabled: {
|
|
122
122
|
source: 'settings',
|
|
123
123
|
type: 'boolean',
|
|
124
|
-
description: '
|
|
124
|
+
description: 'Activar dictado local (mantener pulsado para hablar)',
|
|
125
125
|
},
|
|
126
126
|
}
|
|
127
127
|
: {}),
|
|
@@ -25,6 +25,7 @@ function getClipboardCommands() {
|
|
|
25
25
|
win32: join(baseTmpDir, screenshotFilename),
|
|
26
26
|
};
|
|
27
27
|
const screenshotPath = tempPaths[platform] || tempPaths.linux;
|
|
28
|
+
const escapedScreenshotPath = screenshotPath.replace(/'/g, "''");
|
|
28
29
|
// Platform-specific clipboard commands
|
|
29
30
|
const commands = {
|
|
30
31
|
darwin: {
|
|
@@ -40,10 +41,10 @@ function getClipboardCommands() {
|
|
|
40
41
|
deleteFile: `rm -f "${screenshotPath}"`,
|
|
41
42
|
},
|
|
42
43
|
win32: {
|
|
43
|
-
checkImage: 'powershell -NoProfile -Command "
|
|
44
|
-
saveImage: `powershell -NoProfile -Command "
|
|
45
|
-
getPath: 'powershell -NoProfile -Command "Get-Clipboard"',
|
|
46
|
-
deleteFile: `
|
|
44
|
+
checkImage: 'powershell -NoProfile -STA -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Clipboard]::ContainsImage()"',
|
|
45
|
+
saveImage: `powershell -NoProfile -STA -Command "Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img -ne $null) { $img.Save('${escapedScreenshotPath}', [System.Drawing.Imaging.ImageFormat]::Png) } else { exit 1 }"`,
|
|
46
|
+
getPath: 'powershell -NoProfile -Command "Get-Clipboard -Raw"',
|
|
47
|
+
deleteFile: `powershell -NoProfile -Command "Remove-Item -LiteralPath '${escapedScreenshotPath}' -Force -ErrorAction SilentlyContinue"`,
|
|
47
48
|
},
|
|
48
49
|
};
|
|
49
50
|
return {
|
|
@@ -56,7 +57,12 @@ function getClipboardCommands() {
|
|
|
56
57
|
*/
|
|
57
58
|
export async function hasImageInClipboard() {
|
|
58
59
|
if (process.platform !== 'darwin') {
|
|
59
|
-
|
|
60
|
+
const { commands } = getClipboardCommands();
|
|
61
|
+
const result = await execa(commands.checkImage, {
|
|
62
|
+
shell: true,
|
|
63
|
+
reject: false,
|
|
64
|
+
});
|
|
65
|
+
return result.exitCode === 0 && result.stdout.trim().toLowerCase() !== 'false';
|
|
60
66
|
}
|
|
61
67
|
if (feature('NATIVE_CLIPBOARD_IMAGE') &&
|
|
62
68
|
getFeatureValue_CACHED_MAY_BE_STALE('tengu_collage_kaleidoscope', true)) {
|
|
@@ -552,6 +552,8 @@ export function modelDisplayString(model) {
|
|
|
552
552
|
}
|
|
553
553
|
// @[MODEL LAUNCH]: Add a marketing name mapping for the new model below.
|
|
554
554
|
export function getMarketingNameForModel(modelId) {
|
|
555
|
+
if (!modelId)
|
|
556
|
+
return undefined;
|
|
555
557
|
if (getAPIProvider() === 'foundry') {
|
|
556
558
|
// deployment ID is user-defined in Foundry, so it may have no relation to the actual model
|
|
557
559
|
return undefined;
|
|
@@ -501,7 +501,7 @@ export const SettingsSchema = lazySchema(() => z
|
|
|
501
501
|
language: z
|
|
502
502
|
.string()
|
|
503
503
|
.optional()
|
|
504
|
-
.describe('
|
|
504
|
+
.describe('Idioma preferido para las respuestas de Context y el dictado local (por ejemplo, "japanese", "spanish")'),
|
|
505
505
|
skipWebFetchPreflight: z
|
|
506
506
|
.boolean()
|
|
507
507
|
.optional()
|
|
@@ -666,7 +666,7 @@ export const SettingsSchema = lazySchema(() => z
|
|
|
666
666
|
voiceEnabled: z
|
|
667
667
|
.boolean()
|
|
668
668
|
.optional()
|
|
669
|
-
.describe('
|
|
669
|
+
.describe('Activar dictado local (mantener pulsado para hablar)'),
|
|
670
670
|
}
|
|
671
671
|
: {}),
|
|
672
672
|
...(feature('KAIROS')
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { feature } from '../recovery/bunBundleShim.js';
|
|
2
1
|
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js';
|
|
3
|
-
import { getClaudeAIOAuthTokens, isAnthropicAuthEnabled, } from '../utils/auth.js';
|
|
4
2
|
/**
|
|
5
3
|
* Kill-switch check for voice mode. Returns true unless the
|
|
6
4
|
* `tengu_amber_quartz_disabled` GrowthBook flag is flipped on (emergency
|
|
@@ -10,32 +8,14 @@ import { getClaudeAIOAuthTokens, isAnthropicAuthEnabled, } from '../utils/auth.j
|
|
|
10
8
|
* should be *visible* (e.g., command registration, config UI).
|
|
11
9
|
*/
|
|
12
10
|
export function isVoiceGrowthBookEnabled() {
|
|
13
|
-
|
|
14
|
-
// Negative pattern (if (!feature(...)) return) does not eliminate
|
|
15
|
-
// inline string literals from external builds.
|
|
16
|
-
return feature('VOICE_MODE')
|
|
17
|
-
? !getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_quartz_disabled', false)
|
|
18
|
-
: false;
|
|
11
|
+
return !getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_quartz_disabled', false);
|
|
19
12
|
}
|
|
20
13
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* first call spawns `security` on macOS (~20-50ms), subsequent calls are
|
|
24
|
-
* cache hits. The memoize clears on token refresh (~once/hour), so one
|
|
25
|
-
* cold spawn per refresh is expected. Cheap enough for usage-time checks.
|
|
14
|
+
* Keep this helper for compatibility with the existing voice hooks. Local
|
|
15
|
+
* dictation no longer depends on Claude.ai auth.
|
|
26
16
|
*/
|
|
27
17
|
export function hasVoiceAuth() {
|
|
28
|
-
|
|
29
|
-
// endpoint on claude.ai which is not available with API keys,
|
|
30
|
-
// Bedrock, Vertex, or Foundry.
|
|
31
|
-
if (!isAnthropicAuthEnabled()) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
// isAnthropicAuthEnabled only checks the auth *provider*, not whether
|
|
35
|
-
// a token exists. Without this check, the voice UI renders but
|
|
36
|
-
// connectVoiceStream fails silently when the user isn't logged in.
|
|
37
|
-
const tokens = getClaudeAIOAuthTokens();
|
|
38
|
-
return Boolean(tokens?.accessToken);
|
|
18
|
+
return true;
|
|
39
19
|
}
|
|
40
20
|
/**
|
|
41
21
|
* Full runtime check: auth + GrowthBook kill-switch. Callers: `/voice`
|
|
@@ -44,5 +24,5 @@ export function hasVoiceAuth() {
|
|
|
44
24
|
* paths use useVoiceEnabled() instead (memoizes the auth half).
|
|
45
25
|
*/
|
|
46
26
|
export function isVoiceModeEnabled() {
|
|
47
|
-
return
|
|
27
|
+
return isVoiceGrowthBookEnabled();
|
|
48
28
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
let cachedModule = null;
|
|
4
|
+
let loadAttempted = false;
|
|
5
|
+
function loadModule() {
|
|
6
|
+
if (loadAttempted) {
|
|
7
|
+
return cachedModule;
|
|
8
|
+
}
|
|
9
|
+
loadAttempted = true;
|
|
10
|
+
// Supported platforms: macOS (darwin), Linux, Windows (win32)
|
|
11
|
+
const platform = process.platform;
|
|
12
|
+
if (platform !== 'darwin' && platform !== 'linux' && platform !== 'win32') {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
// Candidate 1: native-embed path (bun compile). AUDIO_CAPTURE_NODE_PATH is
|
|
16
|
+
// defined at build time in build-with-plugins.ts for native builds only — the
|
|
17
|
+
// define resolves it to the static literal "../../audio-capture.node" so bun
|
|
18
|
+
// compile can rewrite it to /$bunfs/root/audio-capture.node. MUST stay a
|
|
19
|
+
// direct require(env var) — bun cannot analyze require(variable) from a loop.
|
|
20
|
+
if (process.env.AUDIO_CAPTURE_NODE_PATH) {
|
|
21
|
+
try {
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
23
|
+
cachedModule = require(process.env.AUDIO_CAPTURE_NODE_PATH);
|
|
24
|
+
return cachedModule;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// fall through to runtime fallbacks below
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Candidates 2/3: npm-install and dev/source layouts. Dynamic require is
|
|
31
|
+
// fine here — in bundled output (node --target build) require() resolves at
|
|
32
|
+
// runtime relative to cli.js at the package root; in dev it resolves
|
|
33
|
+
// relative to this file (vendor/audio-capture-src/index.ts).
|
|
34
|
+
const platformDir = `${process.arch}-${platform}`;
|
|
35
|
+
const fallbacks = [
|
|
36
|
+
`./vendor/audio-capture/${platformDir}/audio-capture.node`,
|
|
37
|
+
`../audio-capture/${platformDir}/audio-capture.node`,
|
|
38
|
+
];
|
|
39
|
+
for (const p of fallbacks) {
|
|
40
|
+
try {
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
42
|
+
cachedModule = require(p);
|
|
43
|
+
return cachedModule;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// try next
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
export function isNativeAudioAvailable() {
|
|
52
|
+
return loadModule() !== null;
|
|
53
|
+
}
|
|
54
|
+
export function startNativeRecording(onData, onEnd) {
|
|
55
|
+
const mod = loadModule();
|
|
56
|
+
if (!mod) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return mod.startRecording(onData, onEnd);
|
|
60
|
+
}
|
|
61
|
+
export function stopNativeRecording() {
|
|
62
|
+
const mod = loadModule();
|
|
63
|
+
if (!mod) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
mod.stopRecording();
|
|
67
|
+
}
|
|
68
|
+
export function isNativeRecordingActive() {
|
|
69
|
+
const mod = loadModule();
|
|
70
|
+
if (!mod) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
return mod.isRecording();
|
|
74
|
+
}
|
|
75
|
+
export function startNativePlayback(sampleRate, channels) {
|
|
76
|
+
const mod = loadModule();
|
|
77
|
+
if (!mod) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return mod.startPlayback(sampleRate, channels);
|
|
81
|
+
}
|
|
82
|
+
export function writeNativePlaybackData(data) {
|
|
83
|
+
const mod = loadModule();
|
|
84
|
+
if (!mod) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
mod.writePlaybackData(data);
|
|
88
|
+
}
|
|
89
|
+
export function stopNativePlayback() {
|
|
90
|
+
const mod = loadModule();
|
|
91
|
+
if (!mod) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
mod.stopPlayback();
|
|
95
|
+
}
|
|
96
|
+
export function isNativePlaying() {
|
|
97
|
+
const mod = loadModule();
|
|
98
|
+
if (!mod) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return mod.isPlaying();
|
|
102
|
+
}
|
|
103
|
+
// Returns the microphone authorization status.
|
|
104
|
+
// On macOS, returns the TCC status: 0=notDetermined, 1=restricted, 2=denied, 3=authorized.
|
|
105
|
+
// On Linux, always returns 3 (authorized) — no system-level mic permission API.
|
|
106
|
+
// On Windows, returns 3 (authorized) if registry key absent or allowed, 2 (denied) if explicitly denied.
|
|
107
|
+
// Returns 0 (notDetermined) if the native module is unavailable.
|
|
108
|
+
export function microphoneAuthorizationStatus() {
|
|
109
|
+
const mod = loadModule();
|
|
110
|
+
if (!mod || !mod.microphoneAuthorizationStatus) {
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
return mod.microphoneAuthorizationStatus();
|
|
114
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
|
|
2
|
+
import { createRequire } from 'module'
|
|
3
|
+
|
|
4
|
+
const require = createRequire(import.meta.url)
|
|
5
|
+
|
|
6
|
+
type AudioCaptureNapi = {
|
|
7
|
+
startRecording(
|
|
8
|
+
onData: (data: Buffer) => void,
|
|
9
|
+
onEnd: () => void,
|
|
10
|
+
): boolean
|
|
11
|
+
stopRecording(): void
|
|
12
|
+
isRecording(): boolean
|
|
13
|
+
startPlayback(sampleRate: number, channels: number): boolean
|
|
14
|
+
writePlaybackData(data: Buffer): void
|
|
15
|
+
stopPlayback(): void
|
|
16
|
+
isPlaying(): boolean
|
|
17
|
+
// TCC microphone authorization status (macOS only):
|
|
18
|
+
// 0 = notDetermined, 1 = restricted, 2 = denied, 3 = authorized.
|
|
19
|
+
// Linux: always returns 3 (authorized) — no system-level microphone permission API.
|
|
20
|
+
// Windows: returns 3 (authorized) if registry key absent or allowed,
|
|
21
|
+
// 2 (denied) if microphone access is explicitly denied.
|
|
22
|
+
microphoneAuthorizationStatus?(): number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let cachedModule: AudioCaptureNapi | null = null
|
|
26
|
+
let loadAttempted = false
|
|
27
|
+
|
|
28
|
+
function loadModule(): AudioCaptureNapi | null {
|
|
29
|
+
if (loadAttempted) {
|
|
30
|
+
return cachedModule
|
|
31
|
+
}
|
|
32
|
+
loadAttempted = true
|
|
33
|
+
|
|
34
|
+
// Supported platforms: macOS (darwin), Linux, Windows (win32)
|
|
35
|
+
const platform = process.platform
|
|
36
|
+
if (platform !== 'darwin' && platform !== 'linux' && platform !== 'win32') {
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Candidate 1: native-embed path (bun compile). AUDIO_CAPTURE_NODE_PATH is
|
|
41
|
+
// defined at build time in build-with-plugins.ts for native builds only — the
|
|
42
|
+
// define resolves it to the static literal "../../audio-capture.node" so bun
|
|
43
|
+
// compile can rewrite it to /$bunfs/root/audio-capture.node. MUST stay a
|
|
44
|
+
// direct require(env var) — bun cannot analyze require(variable) from a loop.
|
|
45
|
+
if (process.env.AUDIO_CAPTURE_NODE_PATH) {
|
|
46
|
+
try {
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
48
|
+
cachedModule = require(
|
|
49
|
+
process.env.AUDIO_CAPTURE_NODE_PATH,
|
|
50
|
+
) as AudioCaptureNapi
|
|
51
|
+
return cachedModule
|
|
52
|
+
} catch {
|
|
53
|
+
// fall through to runtime fallbacks below
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Candidates 2/3: npm-install and dev/source layouts. Dynamic require is
|
|
58
|
+
// fine here — in bundled output (node --target build) require() resolves at
|
|
59
|
+
// runtime relative to cli.js at the package root; in dev it resolves
|
|
60
|
+
// relative to this file (vendor/audio-capture-src/index.ts).
|
|
61
|
+
const platformDir = `${process.arch}-${platform}`
|
|
62
|
+
const fallbacks = [
|
|
63
|
+
`./vendor/audio-capture/${platformDir}/audio-capture.node`,
|
|
64
|
+
`../audio-capture/${platformDir}/audio-capture.node`,
|
|
65
|
+
]
|
|
66
|
+
for (const p of fallbacks) {
|
|
67
|
+
try {
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
69
|
+
cachedModule = require(p) as AudioCaptureNapi
|
|
70
|
+
return cachedModule
|
|
71
|
+
} catch {
|
|
72
|
+
// try next
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function isNativeAudioAvailable(): boolean {
|
|
79
|
+
return loadModule() !== null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function startNativeRecording(
|
|
83
|
+
onData: (data: Buffer) => void,
|
|
84
|
+
onEnd: () => void,
|
|
85
|
+
): boolean {
|
|
86
|
+
const mod = loadModule()
|
|
87
|
+
if (!mod) {
|
|
88
|
+
return false
|
|
89
|
+
}
|
|
90
|
+
return mod.startRecording(onData, onEnd)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function stopNativeRecording(): void {
|
|
94
|
+
const mod = loadModule()
|
|
95
|
+
if (!mod) {
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
mod.stopRecording()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function isNativeRecordingActive(): boolean {
|
|
102
|
+
const mod = loadModule()
|
|
103
|
+
if (!mod) {
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
return mod.isRecording()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function startNativePlayback(
|
|
110
|
+
sampleRate: number,
|
|
111
|
+
channels: number,
|
|
112
|
+
): boolean {
|
|
113
|
+
const mod = loadModule()
|
|
114
|
+
if (!mod) {
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
117
|
+
return mod.startPlayback(sampleRate, channels)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function writeNativePlaybackData(data: Buffer): void {
|
|
121
|
+
const mod = loadModule()
|
|
122
|
+
if (!mod) {
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
mod.writePlaybackData(data)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function stopNativePlayback(): void {
|
|
129
|
+
const mod = loadModule()
|
|
130
|
+
if (!mod) {
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
mod.stopPlayback()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function isNativePlaying(): boolean {
|
|
137
|
+
const mod = loadModule()
|
|
138
|
+
if (!mod) {
|
|
139
|
+
return false
|
|
140
|
+
}
|
|
141
|
+
return mod.isPlaying()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Returns the microphone authorization status.
|
|
145
|
+
// On macOS, returns the TCC status: 0=notDetermined, 1=restricted, 2=denied, 3=authorized.
|
|
146
|
+
// On Linux, always returns 3 (authorized) — no system-level mic permission API.
|
|
147
|
+
// On Windows, returns 3 (authorized) if registry key absent or allowed, 2 (denied) if explicitly denied.
|
|
148
|
+
// Returns 0 (notDetermined) if the native module is unavailable.
|
|
149
|
+
export function microphoneAuthorizationStatus(): number {
|
|
150
|
+
const mod = loadModule()
|
|
151
|
+
if (!mod || !mod.microphoneAuthorizationStatus) {
|
|
152
|
+
return 0
|
|
153
|
+
}
|
|
154
|
+
return mod.microphoneAuthorizationStatus()
|
|
155
|
+
}
|