@remcp/runtime 0.2.124 → 0.2.126
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 +12 -0
- package/package.json +1 -1
- package/src/extended/diagnostics.mjs +121 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.126 — 2026-10-01
|
|
4
|
+
|
|
5
|
+
- macOS `network` route inventory now uses `/usr/sbin/netstat -rn`; the previous generic Unix fallback invoked `route -n`, which on BSD/macOS expects a command and can return usage instead of the table.
|
|
6
|
+
- Linux and Windows network backends are unchanged.
|
|
7
|
+
- The public macOS release gate executes the real route handler on `macos-latest`. The 83 tool names/schemas remain unchanged.
|
|
8
|
+
|
|
9
|
+
## 0.2.125 — 2026-10-01
|
|
10
|
+
|
|
11
|
+
- Windows `audio` now controls the default render endpoint through the native Core Audio `IAudioEndpointVolume` interface hosted by inbox PowerShell `Add-Type`.
|
|
12
|
+
- `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.
|
|
13
|
+
- 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.
|
|
14
|
+
|
|
3
15
|
## 0.2.124 — 2026-10-01
|
|
4
16
|
|
|
5
17
|
- 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
|
@@ -135,6 +135,9 @@ export async function networkTool(args) {
|
|
|
135
135
|
: 'Get-NetTCPConnection -State Listen | Select-Object LocalAddress,LocalPort,OwningProcess,State | Sort-Object LocalPort | ConvertTo-Json -Compress';
|
|
136
136
|
return text((await runPowerShell(script, { label:`network ${action}` })).stdout.trim() || '[]');
|
|
137
137
|
}
|
|
138
|
+
if (process.platform === 'darwin' && action === 'routes') {
|
|
139
|
+
return text((await runFile('/usr/sbin/netstat', ['-rn'], { label:'routes' })).stdout);
|
|
140
|
+
}
|
|
138
141
|
if (action === 'routes') {
|
|
139
142
|
if (commandExists('ip')) return text((await runFile('ip', ['route','show'], { label:'routes' })).stdout);
|
|
140
143
|
if (commandExists('route')) return text((await runFile('route', ['-n'], { label:'routes' })).stdout);
|
|
@@ -183,6 +186,117 @@ export async function environmentTool(args) {
|
|
|
183
186
|
});
|
|
184
187
|
}
|
|
185
188
|
|
|
189
|
+
const WINDOWS_AUDIO_CORE_CSHARP = String.raw`
|
|
190
|
+
using System;
|
|
191
|
+
using System.Runtime.InteropServices;
|
|
192
|
+
|
|
193
|
+
namespace ReMCP {
|
|
194
|
+
enum EDataFlow { eRender = 0, eCapture = 1, eAll = 2 }
|
|
195
|
+
enum ERole { eConsole = 0, eMultimedia = 1, eCommunications = 2 }
|
|
196
|
+
|
|
197
|
+
[ComImport]
|
|
198
|
+
[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
|
|
199
|
+
class MMDeviceEnumeratorComObject { }
|
|
200
|
+
|
|
201
|
+
[ComImport]
|
|
202
|
+
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
|
|
203
|
+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
204
|
+
interface IMMDeviceEnumerator {
|
|
205
|
+
[PreserveSig] int EnumAudioEndpoints(EDataFlow dataFlow, uint stateMask, out IntPtr devices);
|
|
206
|
+
[PreserveSig] int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice endpoint);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
[ComImport]
|
|
210
|
+
[Guid("D666063F-1587-4E43-81F1-B948E807363F")]
|
|
211
|
+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
212
|
+
interface IMMDevice {
|
|
213
|
+
[PreserveSig] int Activate(ref Guid iid, uint clsCtx, IntPtr activationParams, [MarshalAs(UnmanagedType.IUnknown)] out object instance);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
[ComImport]
|
|
217
|
+
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A")]
|
|
218
|
+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
219
|
+
interface IAudioEndpointVolume {
|
|
220
|
+
[PreserveSig] int RegisterControlChangeNotify(IntPtr notify);
|
|
221
|
+
[PreserveSig] int UnregisterControlChangeNotify(IntPtr notify);
|
|
222
|
+
[PreserveSig] int GetChannelCount(out uint count);
|
|
223
|
+
[PreserveSig] int SetMasterVolumeLevel(float levelDb, ref Guid eventContext);
|
|
224
|
+
[PreserveSig] int SetMasterVolumeLevelScalar(float level, ref Guid eventContext);
|
|
225
|
+
[PreserveSig] int GetMasterVolumeLevel(out float levelDb);
|
|
226
|
+
[PreserveSig] int GetMasterVolumeLevelScalar(out float level);
|
|
227
|
+
[PreserveSig] int SetChannelVolumeLevel(uint channel, float levelDb, ref Guid eventContext);
|
|
228
|
+
[PreserveSig] int SetChannelVolumeLevelScalar(uint channel, float level, ref Guid eventContext);
|
|
229
|
+
[PreserveSig] int GetChannelVolumeLevel(uint channel, out float levelDb);
|
|
230
|
+
[PreserveSig] int GetChannelVolumeLevelScalar(uint channel, out float level);
|
|
231
|
+
[PreserveSig] int SetMute([MarshalAs(UnmanagedType.Bool)] bool muted, ref Guid eventContext);
|
|
232
|
+
[PreserveSig] int GetMute([MarshalAs(UnmanagedType.Bool)] out bool muted);
|
|
233
|
+
[PreserveSig] int GetVolumeStepInfo(out uint step, out uint stepCount);
|
|
234
|
+
[PreserveSig] int VolumeStepUp(ref Guid eventContext);
|
|
235
|
+
[PreserveSig] int VolumeStepDown(ref Guid eventContext);
|
|
236
|
+
[PreserveSig] int QueryHardwareSupport(out uint mask);
|
|
237
|
+
[PreserveSig] int GetVolumeRange(out float minDb, out float maxDb, out float incrementDb);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
public static class WindowsAudio {
|
|
241
|
+
const uint CLSCTX_ALL = 23;
|
|
242
|
+
|
|
243
|
+
static IAudioEndpointVolume Endpoint() {
|
|
244
|
+
IMMDeviceEnumerator enumerator = (IMMDeviceEnumerator)(new MMDeviceEnumeratorComObject());
|
|
245
|
+
IMMDevice device;
|
|
246
|
+
Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out device));
|
|
247
|
+
Guid iid = typeof(IAudioEndpointVolume).GUID;
|
|
248
|
+
object endpoint;
|
|
249
|
+
Marshal.ThrowExceptionForHR(device.Activate(ref iid, CLSCTX_ALL, IntPtr.Zero, out endpoint));
|
|
250
|
+
return (IAudioEndpointVolume)endpoint;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
public static string GetStateJson() {
|
|
254
|
+
IAudioEndpointVolume endpoint = Endpoint();
|
|
255
|
+
float scalar;
|
|
256
|
+
bool muted;
|
|
257
|
+
Marshal.ThrowExceptionForHR(endpoint.GetMasterVolumeLevelScalar(out scalar));
|
|
258
|
+
Marshal.ThrowExceptionForHR(endpoint.GetMute(out muted));
|
|
259
|
+
int volume = (int)Math.Round(Math.Max(0.0f, Math.Min(1.0f, scalar)) * 100.0f, MidpointRounding.AwayFromZero);
|
|
260
|
+
return "{\"volume\":" + volume.ToString() + ",\"muted\":" + (muted ? "true" : "false") + "}";
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
public static void SetVolume(int volume) {
|
|
264
|
+
if (volume < 0 || volume > 100) throw new ArgumentOutOfRangeException("volume");
|
|
265
|
+
IAudioEndpointVolume endpoint = Endpoint();
|
|
266
|
+
Guid context = Guid.Empty;
|
|
267
|
+
Marshal.ThrowExceptionForHR(endpoint.SetMasterVolumeLevelScalar(volume / 100.0f, ref context));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
public static void SetMuted(bool muted) {
|
|
271
|
+
IAudioEndpointVolume endpoint = Endpoint();
|
|
272
|
+
Guid context = Guid.Empty;
|
|
273
|
+
Marshal.ThrowExceptionForHR(endpoint.SetMute(muted, ref context));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
`;
|
|
278
|
+
|
|
279
|
+
export function windowsAudioPowerShell(action = 'status', volume = 50, { compileOnly = false } = {}) {
|
|
280
|
+
const selected = requireEnum(action, 'action', ['status','set_volume','mute','unmute']);
|
|
281
|
+
const normalizedVolume = clamp(volume, 50, 0, 100);
|
|
282
|
+
const mutation = selected === 'set_volume'
|
|
283
|
+
? `[ReMCP.WindowsAudio]::SetVolume(${normalizedVolume})`
|
|
284
|
+
: selected === 'mute'
|
|
285
|
+
? '[ReMCP.WindowsAudio]::SetMuted($true)'
|
|
286
|
+
: selected === 'unmute'
|
|
287
|
+
? '[ReMCP.WindowsAudio]::SetMuted($false)'
|
|
288
|
+
: '';
|
|
289
|
+
return [
|
|
290
|
+
"$ErrorActionPreference='Stop'",
|
|
291
|
+
"$source=@'",
|
|
292
|
+
WINDOWS_AUDIO_CORE_CSHARP,
|
|
293
|
+
"'@",
|
|
294
|
+
'Add-Type -TypeDefinition $source -Language CSharp -ErrorAction Stop',
|
|
295
|
+
compileOnly ? "Write-Output 'compiled'" : mutation,
|
|
296
|
+
compileOnly ? '' : '[Console]::Out.WriteLine([ReMCP.WindowsAudio]::GetStateJson())',
|
|
297
|
+
].filter(Boolean).join('\n');
|
|
298
|
+
}
|
|
299
|
+
|
|
186
300
|
export async function audioTool(args) {
|
|
187
301
|
const action = requireEnum(args.action || 'status', 'action', ['status','set_volume','mute','unmute']);
|
|
188
302
|
const volume = clamp(args.volume, 50, 0, 100);
|
|
@@ -196,12 +310,13 @@ export async function audioTool(args) {
|
|
|
196
310
|
}
|
|
197
311
|
if (process.platform === 'win32') {
|
|
198
312
|
if (!commandExists('powershell.exe') && !process.env.SystemRoot) unavailable('Windows audio control');
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
313
|
+
const { stdout } = await runPowerShell(windowsAudioPowerShell(action, volume), { label:'Windows CoreAudio control', timeout:30_000 });
|
|
314
|
+
const rendered = stdout.trim();
|
|
315
|
+
try {
|
|
316
|
+
return jsonResult(JSON.parse(rendered));
|
|
317
|
+
} catch {
|
|
318
|
+
throw new Error(`Windows CoreAudio returned an invalid state payload: ${rendered || '(empty)'}`);
|
|
319
|
+
}
|
|
205
320
|
}
|
|
206
321
|
if (commandExists('wpctl')) {
|
|
207
322
|
if (action === 'status') return text((await runFile('wpctl', ['get-volume','@DEFAULT_AUDIO_SINK@'], { label:'audio status' })).stdout.trim());
|