@remcp/runtime 0.2.124 → 0.2.125
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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/extended/diagnostics.mjs +118 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.125 — 2026-10-01
|
|
4
|
+
|
|
5
|
+
- Windows `audio` now controls the default render endpoint through the native Core Audio `IAudioEndpointVolume` interface hosted by inbox PowerShell `Add-Type`.
|
|
6
|
+
- `status` returns the actual 0–100 master level and mute state; `set_volume` is absolute; `mute`/`unmute` use idempotent `SetMute(true/false)` instead of a shared toggle key.
|
|
7
|
+
- Windows release CI compiles the interop definition with PowerShell 5.1 on `windows-latest`. No third-party runtime dependency is added and the 83 tool schemas are unchanged.
|
|
8
|
+
|
|
3
9
|
## 0.2.124 — 2026-10-01
|
|
4
10
|
|
|
5
11
|
- P3 document capability discovery is cross-platform: Windows uses the native .NET OOXML archive backend, while macOS/Linux require both `zip` and `unzip` before advertising the XLSX/DOCX editor tools.
|
package/package.json
CHANGED
|
@@ -183,6 +183,117 @@ export async function environmentTool(args) {
|
|
|
183
183
|
});
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
const WINDOWS_AUDIO_CORE_CSHARP = String.raw`
|
|
187
|
+
using System;
|
|
188
|
+
using System.Runtime.InteropServices;
|
|
189
|
+
|
|
190
|
+
namespace ReMCP {
|
|
191
|
+
enum EDataFlow { eRender = 0, eCapture = 1, eAll = 2 }
|
|
192
|
+
enum ERole { eConsole = 0, eMultimedia = 1, eCommunications = 2 }
|
|
193
|
+
|
|
194
|
+
[ComImport]
|
|
195
|
+
[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
|
|
196
|
+
class MMDeviceEnumeratorComObject { }
|
|
197
|
+
|
|
198
|
+
[ComImport]
|
|
199
|
+
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
|
|
200
|
+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
201
|
+
interface IMMDeviceEnumerator {
|
|
202
|
+
[PreserveSig] int EnumAudioEndpoints(EDataFlow dataFlow, uint stateMask, out IntPtr devices);
|
|
203
|
+
[PreserveSig] int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice endpoint);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
[ComImport]
|
|
207
|
+
[Guid("D666063F-1587-4E43-81F1-B948E807363F")]
|
|
208
|
+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
209
|
+
interface IMMDevice {
|
|
210
|
+
[PreserveSig] int Activate(ref Guid iid, uint clsCtx, IntPtr activationParams, [MarshalAs(UnmanagedType.IUnknown)] out object instance);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
[ComImport]
|
|
214
|
+
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A")]
|
|
215
|
+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
216
|
+
interface IAudioEndpointVolume {
|
|
217
|
+
[PreserveSig] int RegisterControlChangeNotify(IntPtr notify);
|
|
218
|
+
[PreserveSig] int UnregisterControlChangeNotify(IntPtr notify);
|
|
219
|
+
[PreserveSig] int GetChannelCount(out uint count);
|
|
220
|
+
[PreserveSig] int SetMasterVolumeLevel(float levelDb, ref Guid eventContext);
|
|
221
|
+
[PreserveSig] int SetMasterVolumeLevelScalar(float level, ref Guid eventContext);
|
|
222
|
+
[PreserveSig] int GetMasterVolumeLevel(out float levelDb);
|
|
223
|
+
[PreserveSig] int GetMasterVolumeLevelScalar(out float level);
|
|
224
|
+
[PreserveSig] int SetChannelVolumeLevel(uint channel, float levelDb, ref Guid eventContext);
|
|
225
|
+
[PreserveSig] int SetChannelVolumeLevelScalar(uint channel, float level, ref Guid eventContext);
|
|
226
|
+
[PreserveSig] int GetChannelVolumeLevel(uint channel, out float levelDb);
|
|
227
|
+
[PreserveSig] int GetChannelVolumeLevelScalar(uint channel, out float level);
|
|
228
|
+
[PreserveSig] int SetMute([MarshalAs(UnmanagedType.Bool)] bool muted, ref Guid eventContext);
|
|
229
|
+
[PreserveSig] int GetMute([MarshalAs(UnmanagedType.Bool)] out bool muted);
|
|
230
|
+
[PreserveSig] int GetVolumeStepInfo(out uint step, out uint stepCount);
|
|
231
|
+
[PreserveSig] int VolumeStepUp(ref Guid eventContext);
|
|
232
|
+
[PreserveSig] int VolumeStepDown(ref Guid eventContext);
|
|
233
|
+
[PreserveSig] int QueryHardwareSupport(out uint mask);
|
|
234
|
+
[PreserveSig] int GetVolumeRange(out float minDb, out float maxDb, out float incrementDb);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public static class WindowsAudio {
|
|
238
|
+
const uint CLSCTX_ALL = 23;
|
|
239
|
+
|
|
240
|
+
static IAudioEndpointVolume Endpoint() {
|
|
241
|
+
IMMDeviceEnumerator enumerator = (IMMDeviceEnumerator)(new MMDeviceEnumeratorComObject());
|
|
242
|
+
IMMDevice device;
|
|
243
|
+
Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out device));
|
|
244
|
+
Guid iid = typeof(IAudioEndpointVolume).GUID;
|
|
245
|
+
object endpoint;
|
|
246
|
+
Marshal.ThrowExceptionForHR(device.Activate(ref iid, CLSCTX_ALL, IntPtr.Zero, out endpoint));
|
|
247
|
+
return (IAudioEndpointVolume)endpoint;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public static string GetStateJson() {
|
|
251
|
+
IAudioEndpointVolume endpoint = Endpoint();
|
|
252
|
+
float scalar;
|
|
253
|
+
bool muted;
|
|
254
|
+
Marshal.ThrowExceptionForHR(endpoint.GetMasterVolumeLevelScalar(out scalar));
|
|
255
|
+
Marshal.ThrowExceptionForHR(endpoint.GetMute(out muted));
|
|
256
|
+
int volume = (int)Math.Round(Math.Max(0.0f, Math.Min(1.0f, scalar)) * 100.0f, MidpointRounding.AwayFromZero);
|
|
257
|
+
return "{\"volume\":" + volume.ToString() + ",\"muted\":" + (muted ? "true" : "false") + "}";
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public static void SetVolume(int volume) {
|
|
261
|
+
if (volume < 0 || volume > 100) throw new ArgumentOutOfRangeException("volume");
|
|
262
|
+
IAudioEndpointVolume endpoint = Endpoint();
|
|
263
|
+
Guid context = Guid.Empty;
|
|
264
|
+
Marshal.ThrowExceptionForHR(endpoint.SetMasterVolumeLevelScalar(volume / 100.0f, ref context));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public static void SetMuted(bool muted) {
|
|
268
|
+
IAudioEndpointVolume endpoint = Endpoint();
|
|
269
|
+
Guid context = Guid.Empty;
|
|
270
|
+
Marshal.ThrowExceptionForHR(endpoint.SetMute(muted, ref context));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
`;
|
|
275
|
+
|
|
276
|
+
export function windowsAudioPowerShell(action = 'status', volume = 50, { compileOnly = false } = {}) {
|
|
277
|
+
const selected = requireEnum(action, 'action', ['status','set_volume','mute','unmute']);
|
|
278
|
+
const normalizedVolume = clamp(volume, 50, 0, 100);
|
|
279
|
+
const mutation = selected === 'set_volume'
|
|
280
|
+
? `[ReMCP.WindowsAudio]::SetVolume(${normalizedVolume})`
|
|
281
|
+
: selected === 'mute'
|
|
282
|
+
? '[ReMCP.WindowsAudio]::SetMuted($true)'
|
|
283
|
+
: selected === 'unmute'
|
|
284
|
+
? '[ReMCP.WindowsAudio]::SetMuted($false)'
|
|
285
|
+
: '';
|
|
286
|
+
return [
|
|
287
|
+
"$ErrorActionPreference='Stop'",
|
|
288
|
+
"$source=@'",
|
|
289
|
+
WINDOWS_AUDIO_CORE_CSHARP,
|
|
290
|
+
"'@",
|
|
291
|
+
'Add-Type -TypeDefinition $source -Language CSharp -ErrorAction Stop',
|
|
292
|
+
compileOnly ? "Write-Output 'compiled'" : mutation,
|
|
293
|
+
compileOnly ? '' : '[Console]::Out.WriteLine([ReMCP.WindowsAudio]::GetStateJson())',
|
|
294
|
+
].filter(Boolean).join('\n');
|
|
295
|
+
}
|
|
296
|
+
|
|
186
297
|
export async function audioTool(args) {
|
|
187
298
|
const action = requireEnum(args.action || 'status', 'action', ['status','set_volume','mute','unmute']);
|
|
188
299
|
const volume = clamp(args.volume, 50, 0, 100);
|
|
@@ -196,12 +307,13 @@ export async function audioTool(args) {
|
|
|
196
307
|
}
|
|
197
308
|
if (process.platform === 'win32') {
|
|
198
309
|
if (!commandExists('powershell.exe') && !process.env.SystemRoot) unavailable('Windows audio control');
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
310
|
+
const { stdout } = await runPowerShell(windowsAudioPowerShell(action, volume), { label:'Windows CoreAudio control', timeout:30_000 });
|
|
311
|
+
const rendered = stdout.trim();
|
|
312
|
+
try {
|
|
313
|
+
return jsonResult(JSON.parse(rendered));
|
|
314
|
+
} catch {
|
|
315
|
+
throw new Error(`Windows CoreAudio returned an invalid state payload: ${rendered || '(empty)'}`);
|
|
316
|
+
}
|
|
205
317
|
}
|
|
206
318
|
if (commandExists('wpctl')) {
|
|
207
319
|
if (action === 'status') return text((await runFile('wpctl', ['get-volume','@DEFAULT_AUDIO_SINK@'], { label:'audio status' })).stdout.trim());
|