@mokoconsulting/mcp-windows 3.0.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/.gitattributes +94 -0
- package/.gitmessage +9 -0
- package/.mokogitea/ISSUE_TEMPLATE/adr.md +110 -0
- package/.mokogitea/ISSUE_TEMPLATE/bug_report.md +48 -0
- package/.mokogitea/ISSUE_TEMPLATE/config.yml +18 -0
- package/.mokogitea/ISSUE_TEMPLATE/documentation.md +52 -0
- package/.mokogitea/ISSUE_TEMPLATE/feature_request.md +51 -0
- package/.mokogitea/ISSUE_TEMPLATE/mcp_api_integration.md +48 -0
- package/.mokogitea/ISSUE_TEMPLATE/mcp_connection_issue.md +67 -0
- package/.mokogitea/ISSUE_TEMPLATE/mcp_tool_request.md +49 -0
- package/.mokogitea/ISSUE_TEMPLATE/question.md +82 -0
- package/.mokogitea/ISSUE_TEMPLATE/rfc.md +126 -0
- package/.mokogitea/ISSUE_TEMPLATE/security.md +51 -0
- package/.mokogitea/ISSUE_TEMPLATE/version.md +24 -0
- package/.mokogitea/branch-protection.yml +251 -0
- package/.mokogitea/workflows/auto-assign.yml +76 -0
- package/.mokogitea/workflows/auto-bump.yml +66 -0
- package/.mokogitea/workflows/auto-dev-issue.yml +207 -0
- package/.mokogitea/workflows/auto-release.yml +421 -0
- package/.mokogitea/workflows/branch-cleanup.yml +48 -0
- package/.mokogitea/workflows/cascade-dev.yml +10 -0
- package/.mokogitea/workflows/changelog-validation.yml +101 -0
- package/.mokogitea/workflows/ci-generic.yml +191 -0
- package/.mokogitea/workflows/cleanup.yml +87 -0
- package/.mokogitea/workflows/codeql-analysis.yml +115 -0
- package/.mokogitea/workflows/copilot-agent.yml +44 -0
- package/.mokogitea/workflows/deploy-manual.yml +126 -0
- package/.mokogitea/workflows/enterprise-firewall-setup.yml +758 -0
- package/.mokogitea/workflows/gitleaks.yml +92 -0
- package/.mokogitea/workflows/issue-branch.yml +73 -0
- package/.mokogitea/workflows/mcp-auto-release.yml +278 -0
- package/.mokogitea/workflows/mcp-build-test.yml +65 -0
- package/.mokogitea/workflows/mcp-sdk-check.yml +109 -0
- package/.mokogitea/workflows/mcp-tool-inventory.yml +61 -0
- package/.mokogitea/workflows/notify.yml +70 -0
- package/.mokogitea/workflows/npm-publish.yml +113 -0
- package/.mokogitea/workflows/pr-check.yml +534 -0
- package/.mokogitea/workflows/pre-release.yml +252 -0
- package/.mokogitea/workflows/rc-revert.yml +66 -0
- package/.mokogitea/workflows/repo-health.yml +712 -0
- package/.mokogitea/workflows/repository-cleanup.yml +525 -0
- package/.mokogitea/workflows/security-audit.yml +82 -0
- package/.mokogitea/workflows/standards-compliance.yml +2614 -0
- package/.mokogitea/workflows/sync-version-on-merge.yml +133 -0
- package/.mokogitea/workflows/update-server.yml +312 -0
- package/.mokogitea/workflows/workflow-sync-trigger.yml +73 -0
- package/CHANGELOG.md +130 -0
- package/CLAUDE.md +49 -0
- package/CONTRIBUTING.md +161 -0
- package/ISSUES.md +601 -0
- package/Makefile +70 -0
- package/README.md +80 -0
- package/automation/ci-issue-reporter.sh +237 -0
- package/config.example.json +18 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +111 -0
- package/dist/shell.d.ts +50 -0
- package/dist/shell.js +209 -0
- package/dist/tools/apps.d.ts +3 -0
- package/dist/tools/apps.js +63 -0
- package/dist/tools/audio.d.ts +3 -0
- package/dist/tools/audio.js +142 -0
- package/dist/tools/audio_apps.d.ts +3 -0
- package/dist/tools/audio_apps.js +86 -0
- package/dist/tools/automation.d.ts +3 -0
- package/dist/tools/automation.js +261 -0
- package/dist/tools/bluetooth.d.ts +3 -0
- package/dist/tools/bluetooth.js +96 -0
- package/dist/tools/clipboard.d.ts +3 -0
- package/dist/tools/clipboard.js +118 -0
- package/dist/tools/config.d.ts +3 -0
- package/dist/tools/config.js +85 -0
- package/dist/tools/dialog.d.ts +3 -0
- package/dist/tools/dialog.js +72 -0
- package/dist/tools/display.d.ts +3 -0
- package/dist/tools/display.js +256 -0
- package/dist/tools/drives.d.ts +3 -0
- package/dist/tools/drives.js +98 -0
- package/dist/tools/environment.d.ts +3 -0
- package/dist/tools/environment.js +129 -0
- package/dist/tools/execute.d.ts +3 -0
- package/dist/tools/execute.js +28 -0
- package/dist/tools/filesystem.d.ts +3 -0
- package/dist/tools/filesystem.js +230 -0
- package/dist/tools/firewall.d.ts +3 -0
- package/dist/tools/firewall.js +108 -0
- package/dist/tools/hosts.d.ts +3 -0
- package/dist/tools/hosts.js +119 -0
- package/dist/tools/maintenance.d.ts +3 -0
- package/dist/tools/maintenance.js +236 -0
- package/dist/tools/netstat.d.ts +3 -0
- package/dist/tools/netstat.js +56 -0
- package/dist/tools/network.d.ts +3 -0
- package/dist/tools/network.js +70 -0
- package/dist/tools/notification.d.ts +3 -0
- package/dist/tools/notification.js +41 -0
- package/dist/tools/power.d.ts +3 -0
- package/dist/tools/power.js +104 -0
- package/dist/tools/printer.d.ts +3 -0
- package/dist/tools/printer.js +97 -0
- package/dist/tools/process.d.ts +3 -0
- package/dist/tools/process.js +54 -0
- package/dist/tools/process_kill.d.ts +3 -0
- package/dist/tools/process_kill.js +48 -0
- package/dist/tools/recycle_bin.d.ts +3 -0
- package/dist/tools/recycle_bin.js +108 -0
- package/dist/tools/registry.d.ts +3 -0
- package/dist/tools/registry.js +136 -0
- package/dist/tools/scheduler.d.ts +3 -0
- package/dist/tools/scheduler.js +116 -0
- package/dist/tools/service.d.ts +3 -0
- package/dist/tools/service.js +79 -0
- package/dist/tools/startup.d.ts +3 -0
- package/dist/tools/startup.js +159 -0
- package/dist/tools/storage.d.ts +3 -0
- package/dist/tools/storage.js +129 -0
- package/dist/tools/system.d.ts +3 -0
- package/dist/tools/system.js +84 -0
- package/dist/tools/system_mgmt.d.ts +3 -0
- package/dist/tools/system_mgmt.js +174 -0
- package/dist/tools/terminal.d.ts +3 -0
- package/dist/tools/terminal.js +80 -0
- package/dist/tools/theme.d.ts +3 -0
- package/dist/tools/theme.js +165 -0
- package/dist/tools/usb.d.ts +3 -0
- package/dist/tools/usb.js +52 -0
- package/dist/tools/virtual_desktop.d.ts +3 -0
- package/dist/tools/virtual_desktop.js +112 -0
- package/dist/tools/wifi.d.ts +3 -0
- package/dist/tools/wifi.js +136 -0
- package/dist/tools/window.d.ts +3 -0
- package/dist/tools/window.js +189 -0
- package/dist/tools/winget.d.ts +3 -0
- package/dist/tools/winget.js +79 -0
- package/dist/tools/wsl.d.ts +3 -0
- package/dist/tools/wsl.js +99 -0
- package/docs/API.md +63 -0
- package/docs/ARCHITECTURE.md +73 -0
- package/docs/INSTALLATION.md +102 -0
- package/docs/index.md +12 -0
- package/package.json +35 -0
- package/scripts/setup.mjs +123 -0
- package/src/index.ts +125 -0
- package/src/shell.ts +253 -0
- package/src/tools/apps.ts +76 -0
- package/src/tools/audio.ts +161 -0
- package/src/tools/audio_apps.ts +98 -0
- package/src/tools/automation.ts +297 -0
- package/src/tools/bluetooth.ts +114 -0
- package/src/tools/clipboard.ts +138 -0
- package/src/tools/config.ts +105 -0
- package/src/tools/dialog.ts +87 -0
- package/src/tools/display.ts +285 -0
- package/src/tools/drives.ts +124 -0
- package/src/tools/environment.ts +146 -0
- package/src/tools/execute.ts +35 -0
- package/src/tools/filesystem.ts +273 -0
- package/src/tools/firewall.ts +125 -0
- package/src/tools/hosts.ts +135 -0
- package/src/tools/maintenance.ts +299 -0
- package/src/tools/netstat.ts +72 -0
- package/src/tools/network.ts +84 -0
- package/src/tools/notification.ts +50 -0
- package/src/tools/power.ts +123 -0
- package/src/tools/printer.ts +114 -0
- package/src/tools/process.ts +80 -0
- package/src/tools/process_kill.ts +57 -0
- package/src/tools/recycle_bin.ts +126 -0
- package/src/tools/registry.ts +165 -0
- package/src/tools/scheduler.ts +140 -0
- package/src/tools/service.ts +102 -0
- package/src/tools/startup.ts +180 -0
- package/src/tools/storage.ts +141 -0
- package/src/tools/system.ts +99 -0
- package/src/tools/system_mgmt.ts +190 -0
- package/src/tools/terminal.ts +117 -0
- package/src/tools/theme.ts +205 -0
- package/src/tools/usb.ts +65 -0
- package/src/tools/virtual_desktop.ts +122 -0
- package/src/tools/wifi.ts +157 -0
- package/src/tools/window.ts +211 -0
- package/src/tools/winget.ts +100 -0
- package/src/tools/wsl.ts +112 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
2
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
*
|
|
4
|
+
* Tool: windows_process_list — List running processes (#2)
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { runPowerShell } from '../shell.js';
|
|
8
|
+
export function registerProcessTools(server) {
|
|
9
|
+
server.tool('windows_process_list', 'List running processes with PID, name, CPU, memory, window title, and path.', {
|
|
10
|
+
filter: z.string().optional().describe('Filter by process name (substring match)'),
|
|
11
|
+
sort: z.enum(['cpu', 'memory', 'name']).default('memory').describe('Sort order'),
|
|
12
|
+
limit: z.number().default(50).describe('Max results to return'),
|
|
13
|
+
}, async ({ filter, sort, limit }) => {
|
|
14
|
+
const filterClause = filter
|
|
15
|
+
? `| Where-Object { $_.Name -like '*${filter.replace(/'/g, "''")}*' }`
|
|
16
|
+
: '';
|
|
17
|
+
const sortClause = sort === 'cpu'
|
|
18
|
+
? '| Sort-Object CPU -Descending'
|
|
19
|
+
: sort === 'name'
|
|
20
|
+
? '| Sort-Object Name'
|
|
21
|
+
: '| Sort-Object WorkingSet64 -Descending';
|
|
22
|
+
const ps = `
|
|
23
|
+
Get-Process ${filterClause} ${sortClause} | Select-Object -First ${limit} |
|
|
24
|
+
ForEach-Object {
|
|
25
|
+
[PSCustomObject]@{
|
|
26
|
+
PID = $_.Id
|
|
27
|
+
Name = $_.ProcessName
|
|
28
|
+
CPU = [math]::Round($_.CPU, 1)
|
|
29
|
+
MemoryMB = [math]::Round($_.WorkingSet64 / 1MB, 1)
|
|
30
|
+
WindowTitle = $_.MainWindowTitle
|
|
31
|
+
Path = $_.Path
|
|
32
|
+
}
|
|
33
|
+
} | ConvertTo-Json -Depth 3 -Compress`;
|
|
34
|
+
const result = await runPowerShell(ps);
|
|
35
|
+
if (result.exitCode !== 0) {
|
|
36
|
+
return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
|
|
37
|
+
}
|
|
38
|
+
let processes = [];
|
|
39
|
+
if (result.stdout) {
|
|
40
|
+
const parsed = JSON.parse(result.stdout);
|
|
41
|
+
processes = Array.isArray(parsed) ? parsed : [parsed];
|
|
42
|
+
}
|
|
43
|
+
const lines = processes.map(p => `${String(p.PID).padStart(7)} ${p.Name.padEnd(25).slice(0, 25)} ${String(p.CPU ?? 0).padStart(8)}s ${String(p.MemoryMB).padStart(8)} MB ${p.WindowTitle || ''}`);
|
|
44
|
+
const header = `${'PID'.padStart(7)} ${'Name'.padEnd(25)} ${'CPU'.padStart(8)} ${'Memory'.padStart(8)} Window Title`;
|
|
45
|
+
const separator = '-'.repeat(90);
|
|
46
|
+
return {
|
|
47
|
+
content: [{
|
|
48
|
+
type: 'text',
|
|
49
|
+
text: `${header}\n${separator}\n${lines.join('\n')}\n\n${processes.length} processes`,
|
|
50
|
+
}],
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=process.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
2
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
*
|
|
4
|
+
* Tool: windows_process_kill (#3)
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { runPowerShell } from '../shell.js';
|
|
8
|
+
export function registerProcessKillTools(server) {
|
|
9
|
+
server.tool('windows_process_kill', 'Terminate running processes by PID or name.', {
|
|
10
|
+
pid: z.number().optional().describe('Process ID to kill'),
|
|
11
|
+
name: z.string().optional().describe('Process name to kill (all matching)'),
|
|
12
|
+
force: z.boolean().default(false).describe('Force termination'),
|
|
13
|
+
}, async ({ pid, name, force }) => {
|
|
14
|
+
if (!pid && !name) {
|
|
15
|
+
return { content: [{ type: 'text', text: 'Provide either pid or name.' }], isError: true };
|
|
16
|
+
}
|
|
17
|
+
const forceFlag = force ? ' -Force' : '';
|
|
18
|
+
let ps;
|
|
19
|
+
if (pid) {
|
|
20
|
+
ps = `
|
|
21
|
+
$p = Get-Process -Id ${pid} -ErrorAction SilentlyContinue
|
|
22
|
+
if ($p) {
|
|
23
|
+
$n = $p.ProcessName
|
|
24
|
+
Stop-Process -Id ${pid}${forceFlag} -ErrorAction Stop
|
|
25
|
+
"Killed PID ${pid} ($n)"
|
|
26
|
+
} else {
|
|
27
|
+
"No process with PID ${pid}"
|
|
28
|
+
}`;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
ps = `
|
|
32
|
+
$procs = Get-Process -Name '${name.replace(/'/g, "''")}' -ErrorAction SilentlyContinue
|
|
33
|
+
if ($procs) {
|
|
34
|
+
$count = @($procs).Count
|
|
35
|
+
$procs | Stop-Process${forceFlag} -ErrorAction Stop
|
|
36
|
+
"Killed $count process(es) named '${name}'"
|
|
37
|
+
} else {
|
|
38
|
+
"No processes named '${name}'"
|
|
39
|
+
}`;
|
|
40
|
+
}
|
|
41
|
+
const result = await runPowerShell(ps);
|
|
42
|
+
return {
|
|
43
|
+
content: [{ type: 'text', text: result.stdout || result.stderr }],
|
|
44
|
+
isError: result.exitCode !== 0,
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=process_kill.js.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
2
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
*
|
|
4
|
+
* Tool: windows_recycle_bin (#26)
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { runPowerShell } from '../shell.js';
|
|
8
|
+
export function registerRecycleBinTools(server) {
|
|
9
|
+
server.tool('windows_recycle_bin', 'Manage the Recycle Bin: list items, get size, restore items, or empty.', {
|
|
10
|
+
action: z.enum(['list', 'size', 'empty', 'restore']).default('list').describe('Action'),
|
|
11
|
+
filter: z.string().optional().describe('Filter by filename (for list/restore)'),
|
|
12
|
+
limit: z.number().default(30).describe('Max items (for list)'),
|
|
13
|
+
}, async ({ action, filter, limit }) => {
|
|
14
|
+
switch (action) {
|
|
15
|
+
case 'size': {
|
|
16
|
+
const ps = `
|
|
17
|
+
$shell = New-Object -ComObject Shell.Application
|
|
18
|
+
$bin = $shell.Namespace(10)
|
|
19
|
+
$items = $bin.Items()
|
|
20
|
+
$count = $items.Count
|
|
21
|
+
$totalSize = 0
|
|
22
|
+
for ($i = 0; $i -lt $count; $i++) {
|
|
23
|
+
$totalSize += $bin.GetDetailsOf($items.Item($i), 2) -replace '[^0-9]','' -as [long]
|
|
24
|
+
}
|
|
25
|
+
[PSCustomObject]@{
|
|
26
|
+
Count = $count
|
|
27
|
+
SizeMB = [math]::Round($totalSize / 1MB, 1)
|
|
28
|
+
} | ConvertTo-Json -Compress`;
|
|
29
|
+
const result = await runPowerShell(ps, { timeout: 15000 });
|
|
30
|
+
if (result.exitCode !== 0) {
|
|
31
|
+
return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
|
|
32
|
+
}
|
|
33
|
+
const info = JSON.parse(result.stdout);
|
|
34
|
+
return { content: [{ type: 'text', text: `Recycle Bin: ${info.Count} items, ${info.SizeMB} MB` }] };
|
|
35
|
+
}
|
|
36
|
+
case 'list': {
|
|
37
|
+
const filterClause = filter
|
|
38
|
+
? `| Where-Object { $_.Name -like '*${filter.replace(/'/g, "''")}*' }`
|
|
39
|
+
: '';
|
|
40
|
+
const ps = `
|
|
41
|
+
$shell = New-Object -ComObject Shell.Application
|
|
42
|
+
$bin = $shell.Namespace(10)
|
|
43
|
+
$items = @()
|
|
44
|
+
foreach ($item in $bin.Items()) {
|
|
45
|
+
$items += [PSCustomObject]@{
|
|
46
|
+
Name = $item.Name
|
|
47
|
+
OriginalPath = $bin.GetDetailsOf($item, 1)
|
|
48
|
+
Size = $bin.GetDetailsOf($item, 2)
|
|
49
|
+
DeletedDate = $bin.GetDetailsOf($item, 3)
|
|
50
|
+
Type = $bin.GetDetailsOf($item, 4)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
$items ${filterClause} | Select-Object -First ${limit} | ConvertTo-Json -Depth 3 -Compress`;
|
|
54
|
+
const result = await runPowerShell(ps, { timeout: 20000 });
|
|
55
|
+
if (result.exitCode !== 0) {
|
|
56
|
+
return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
|
|
57
|
+
}
|
|
58
|
+
if (!result.stdout) {
|
|
59
|
+
return { content: [{ type: 'text', text: 'Recycle Bin is empty.' }] };
|
|
60
|
+
}
|
|
61
|
+
const items = Array.isArray(JSON.parse(result.stdout)) ? JSON.parse(result.stdout) : [JSON.parse(result.stdout)];
|
|
62
|
+
const lines = items.map((i) => `${(i.Name || '').padEnd(35).slice(0, 35)} ${(i.Size || '').padStart(10)} ${(i.DeletedDate || '').padEnd(20)} ${(i.OriginalPath || '').slice(0, 40)}`);
|
|
63
|
+
const header = `${'Name'.padEnd(35)} ${'Size'.padStart(10)} ${'Deleted'.padEnd(20)} Original Path`;
|
|
64
|
+
return {
|
|
65
|
+
content: [{ type: 'text', text: `${header}\n${'─'.repeat(110)}\n${lines.join('\n')}\n\n${items.length} items` }],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
case 'empty': {
|
|
69
|
+
const ps = `
|
|
70
|
+
$shell = New-Object -ComObject Shell.Application
|
|
71
|
+
$count = $shell.Namespace(10).Items().Count
|
|
72
|
+
if ($count -eq 0) { "Recycle Bin is already empty." }
|
|
73
|
+
else {
|
|
74
|
+
Clear-RecycleBin -Force -ErrorAction Stop
|
|
75
|
+
"Emptied Recycle Bin ($count items removed)"
|
|
76
|
+
}`;
|
|
77
|
+
const result = await runPowerShell(ps, { timeout: 15000 });
|
|
78
|
+
return {
|
|
79
|
+
content: [{ type: 'text', text: result.stdout || result.stderr }],
|
|
80
|
+
isError: result.exitCode !== 0,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
case 'restore': {
|
|
84
|
+
if (!filter) {
|
|
85
|
+
return { content: [{ type: 'text', text: 'Restore requires a filter to identify which item(s) to restore.' }], isError: true };
|
|
86
|
+
}
|
|
87
|
+
const ps = `
|
|
88
|
+
$shell = New-Object -ComObject Shell.Application
|
|
89
|
+
$bin = $shell.Namespace(10)
|
|
90
|
+
$restored = 0
|
|
91
|
+
foreach ($item in $bin.Items()) {
|
|
92
|
+
if ($item.Name -like '*${filter.replace(/'/g, "''")}*') {
|
|
93
|
+
$origPath = $bin.GetDetailsOf($item, 1)
|
|
94
|
+
$item.InvokeVerb('undelete')
|
|
95
|
+
$restored++
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if ($restored -gt 0) { "Restored $restored item(s)" } else { "No matching items found" }`;
|
|
99
|
+
const result = await runPowerShell(ps, { timeout: 15000 });
|
|
100
|
+
return {
|
|
101
|
+
content: [{ type: 'text', text: result.stdout || result.stderr }],
|
|
102
|
+
isError: result.exitCode !== 0,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=recycle_bin.js.map
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
2
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
*
|
|
4
|
+
* Tools: windows_registry_read (#29), windows_registry_write (#30)
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { runPowerShell } from '../shell.js';
|
|
8
|
+
function expandHive(path) {
|
|
9
|
+
return path
|
|
10
|
+
.replace(/^HKLM[:\\\/]/i, 'HKLM:\\')
|
|
11
|
+
.replace(/^HKCU[:\\\/]/i, 'HKCU:\\')
|
|
12
|
+
.replace(/^HKCR[:\\\/]/i, 'HKCR:\\')
|
|
13
|
+
.replace(/^HKU[:\\\/]/i, 'HKU:\\')
|
|
14
|
+
.replace(/^HKCC[:\\\/]/i, 'HKCC:\\');
|
|
15
|
+
}
|
|
16
|
+
export function registerRegistryTools(server) {
|
|
17
|
+
server.tool('windows_registry_read', 'Read Windows Registry keys, subkeys, and values. Supports HKLM, HKCU, HKCR abbreviations.', {
|
|
18
|
+
path: z.string().describe('Registry path (e.g. "HKCU:\\Software\\Microsoft")'),
|
|
19
|
+
value: z.string().optional().describe('Specific value name to read (omit to list all values)'),
|
|
20
|
+
subkeys: z.boolean().default(false).describe('List subkeys instead of values'),
|
|
21
|
+
}, async ({ path, value, subkeys }) => {
|
|
22
|
+
const regPath = expandHive(path);
|
|
23
|
+
if (subkeys) {
|
|
24
|
+
const ps = `
|
|
25
|
+
Get-ChildItem -Path '${regPath.replace(/'/g, "''")}' -ErrorAction Stop | ForEach-Object {
|
|
26
|
+
[PSCustomObject]@{
|
|
27
|
+
Name = $_.PSChildName
|
|
28
|
+
SubKeyCount = $_.SubKeyCount
|
|
29
|
+
ValueCount = $_.ValueCount
|
|
30
|
+
}
|
|
31
|
+
} | ConvertTo-Json -Depth 3 -Compress`;
|
|
32
|
+
const result = await runPowerShell(ps, { timeout: 10000 });
|
|
33
|
+
if (result.exitCode !== 0) {
|
|
34
|
+
return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
|
|
35
|
+
}
|
|
36
|
+
if (!result.stdout) {
|
|
37
|
+
return { content: [{ type: 'text', text: 'No subkeys found.' }] };
|
|
38
|
+
}
|
|
39
|
+
const keys = Array.isArray(JSON.parse(result.stdout)) ? JSON.parse(result.stdout) : [JSON.parse(result.stdout)];
|
|
40
|
+
const lines = keys.map((k) => ` ${k.Name.padEnd(40)} ${k.SubKeyCount} subkeys, ${k.ValueCount} values`);
|
|
41
|
+
return { content: [{ type: 'text', text: `${regPath}\n${'─'.repeat(70)}\n${lines.join('\n')}` }] };
|
|
42
|
+
}
|
|
43
|
+
if (value) {
|
|
44
|
+
const ps = `
|
|
45
|
+
$v = Get-ItemProperty -Path '${regPath.replace(/'/g, "''")}' -Name '${value.replace(/'/g, "''")}' -ErrorAction Stop
|
|
46
|
+
$raw = $v.'${value.replace(/'/g, "''")}'
|
|
47
|
+
$kind = (Get-Item -Path '${regPath.replace(/'/g, "''")}' -ErrorAction Stop).GetValueKind('${value.replace(/'/g, "''")}')
|
|
48
|
+
[PSCustomObject]@{
|
|
49
|
+
Name = '${value.replace(/'/g, "''")}'
|
|
50
|
+
Type = $kind.ToString()
|
|
51
|
+
Value = $raw
|
|
52
|
+
} | ConvertTo-Json -Depth 3 -Compress`;
|
|
53
|
+
const result = await runPowerShell(ps, { timeout: 10000 });
|
|
54
|
+
if (result.exitCode !== 0) {
|
|
55
|
+
return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
|
|
56
|
+
}
|
|
57
|
+
const v = JSON.parse(result.stdout);
|
|
58
|
+
return { content: [{ type: 'text', text: `${regPath}\\${v.Name}\nType: ${v.Type}\nValue: ${JSON.stringify(v.Value)}` }] };
|
|
59
|
+
}
|
|
60
|
+
// List all values
|
|
61
|
+
const ps = `
|
|
62
|
+
$key = Get-Item -Path '${regPath.replace(/'/g, "''")}' -ErrorAction Stop
|
|
63
|
+
$key.GetValueNames() | ForEach-Object {
|
|
64
|
+
$name = $_
|
|
65
|
+
$val = $key.GetValue($name)
|
|
66
|
+
$kind = $key.GetValueKind($name)
|
|
67
|
+
[PSCustomObject]@{
|
|
68
|
+
Name = if ($name) { $name } else { '(Default)' }
|
|
69
|
+
Type = $kind.ToString()
|
|
70
|
+
Value = $val
|
|
71
|
+
}
|
|
72
|
+
} | ConvertTo-Json -Depth 3 -Compress`;
|
|
73
|
+
const result = await runPowerShell(ps, { timeout: 10000 });
|
|
74
|
+
if (result.exitCode !== 0) {
|
|
75
|
+
return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
|
|
76
|
+
}
|
|
77
|
+
if (!result.stdout) {
|
|
78
|
+
return { content: [{ type: 'text', text: 'No values found.' }] };
|
|
79
|
+
}
|
|
80
|
+
const values = Array.isArray(JSON.parse(result.stdout)) ? JSON.parse(result.stdout) : [JSON.parse(result.stdout)];
|
|
81
|
+
const lines = values.map((v) => {
|
|
82
|
+
const valStr = typeof v.Value === 'string' ? v.Value : JSON.stringify(v.Value);
|
|
83
|
+
return ` ${v.Name.padEnd(30)} ${v.Type.padEnd(12)} ${valStr.slice(0, 60)}`;
|
|
84
|
+
});
|
|
85
|
+
const header = ` ${'Name'.padEnd(30)} ${'Type'.padEnd(12)} Value`;
|
|
86
|
+
return { content: [{ type: 'text', text: `${regPath}\n${header}\n${'─'.repeat(80)}\n${lines.join('\n')}` }] };
|
|
87
|
+
});
|
|
88
|
+
server.tool('windows_registry_write', 'Write Windows Registry values. Restricted to HKCU by default. Use hklm_override for HKLM writes.', {
|
|
89
|
+
path: z.string().describe('Registry path'),
|
|
90
|
+
name: z.string().describe('Value name'),
|
|
91
|
+
value: z.string().describe('Value data'),
|
|
92
|
+
type: z.enum(['String', 'DWord', 'QWord', 'Binary', 'ExpandString', 'MultiString']).default('String').describe('Value type'),
|
|
93
|
+
hklm_override: z.boolean().default(false).describe('Allow writing to HKLM (requires elevation)'),
|
|
94
|
+
action: z.enum(['set', 'delete', 'create_key', 'delete_key']).default('set').describe('Action'),
|
|
95
|
+
}, async ({ path, name, value, type, hklm_override, action }) => {
|
|
96
|
+
const regPath = expandHive(path);
|
|
97
|
+
// Safety check
|
|
98
|
+
if (regPath.startsWith('HKLM:') && !hklm_override) {
|
|
99
|
+
return {
|
|
100
|
+
content: [{ type: 'text', text: 'HKLM writes are restricted. Set hklm_override=true and ensure elevation.' }],
|
|
101
|
+
isError: true,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
let ps;
|
|
105
|
+
switch (action) {
|
|
106
|
+
case 'set': {
|
|
107
|
+
const typeMap = {
|
|
108
|
+
String: 'String', DWord: 'DWord', QWord: 'QWord',
|
|
109
|
+
Binary: 'Binary', ExpandString: 'ExpandString', MultiString: 'MultiString',
|
|
110
|
+
};
|
|
111
|
+
ps = `
|
|
112
|
+
if (-not (Test-Path '${regPath.replace(/'/g, "''")}')) {
|
|
113
|
+
New-Item -Path '${regPath.replace(/'/g, "''")}' -Force | Out-Null
|
|
114
|
+
}
|
|
115
|
+
Set-ItemProperty -Path '${regPath.replace(/'/g, "''")}' -Name '${name.replace(/'/g, "''")}' -Value '${value.replace(/'/g, "''")}' -Type ${typeMap[type]} -ErrorAction Stop
|
|
116
|
+
"Set ${regPath}\\${name} = ${value} (${type})"`;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case 'delete':
|
|
120
|
+
ps = `Remove-ItemProperty -Path '${regPath.replace(/'/g, "''")}' -Name '${name.replace(/'/g, "''")}' -ErrorAction Stop; "Deleted ${regPath}\\${name}"`;
|
|
121
|
+
break;
|
|
122
|
+
case 'create_key':
|
|
123
|
+
ps = `New-Item -Path '${regPath.replace(/'/g, "''")}' -Force -ErrorAction Stop | Out-Null; "Created key ${regPath}"`;
|
|
124
|
+
break;
|
|
125
|
+
case 'delete_key':
|
|
126
|
+
ps = `Remove-Item -Path '${regPath.replace(/'/g, "''")}' -Recurse -Force -ErrorAction Stop; "Deleted key ${regPath}"`;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
const result = await runPowerShell(ps, { timeout: 10000 });
|
|
130
|
+
return {
|
|
131
|
+
content: [{ type: 'text', text: result.stdout || result.stderr }],
|
|
132
|
+
isError: result.exitCode !== 0,
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
2
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
*
|
|
4
|
+
* Tools: windows_task_scheduler_list (#27), windows_task_scheduler_manage (#28)
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { runPowerShell } from '../shell.js';
|
|
8
|
+
export function registerSchedulerTools(server) {
|
|
9
|
+
server.tool('windows_task_scheduler_list', 'List Windows Task Scheduler tasks with status, last/next run, and trigger type.', {
|
|
10
|
+
folder: z.string().default('\\').describe('Task folder path (e.g. "\\" for root, "\\Microsoft\\")'),
|
|
11
|
+
filter: z.string().optional().describe('Filter by task name (substring)'),
|
|
12
|
+
}, async ({ folder, filter }) => {
|
|
13
|
+
const filterClause = filter
|
|
14
|
+
? `| Where-Object { $_.TaskName -like '*${filter.replace(/'/g, "''")}*' }`
|
|
15
|
+
: '';
|
|
16
|
+
const ps = `
|
|
17
|
+
Get-ScheduledTask -TaskPath '${folder.replace(/'/g, "''")}*' -ErrorAction SilentlyContinue ${filterClause} | ForEach-Object {
|
|
18
|
+
$info = $_ | Get-ScheduledTaskInfo -ErrorAction SilentlyContinue
|
|
19
|
+
[PSCustomObject]@{
|
|
20
|
+
Name = $_.TaskName
|
|
21
|
+
Path = $_.TaskPath
|
|
22
|
+
State = $_.State.ToString()
|
|
23
|
+
LastRun = if ($info.LastRunTime -and $info.LastRunTime.Year -gt 1999) { $info.LastRunTime.ToString('yyyy-MM-dd HH:mm') } else { 'Never' }
|
|
24
|
+
NextRun = if ($info.NextRunTime -and $info.NextRunTime.Year -gt 1999) { $info.NextRunTime.ToString('yyyy-MM-dd HH:mm') } else { 'N/A' }
|
|
25
|
+
LastResult = if ($info) { '0x{0:X}' -f $info.LastTaskResult } else { 'N/A' }
|
|
26
|
+
Triggers = ($_.Triggers | ForEach-Object { $_.CimClass.CimClassName -replace 'MSFT_Task',''-replace 'Trigger','' }) -join ', '
|
|
27
|
+
}
|
|
28
|
+
} | ConvertTo-Json -Depth 3 -Compress`;
|
|
29
|
+
const result = await runPowerShell(ps, { timeout: 30000 });
|
|
30
|
+
if (result.exitCode !== 0) {
|
|
31
|
+
return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
|
|
32
|
+
}
|
|
33
|
+
if (!result.stdout) {
|
|
34
|
+
return { content: [{ type: 'text', text: 'No tasks found.' }] };
|
|
35
|
+
}
|
|
36
|
+
const tasks = Array.isArray(JSON.parse(result.stdout)) ? JSON.parse(result.stdout) : [JSON.parse(result.stdout)];
|
|
37
|
+
const lines = tasks.map((t) => {
|
|
38
|
+
const state = t.State === 'Ready' ? '[RDY]' : t.State === 'Running' ? '[RUN]' : t.State === 'Disabled' ? '[OFF]' : `[${t.State.slice(0, 3).toUpperCase()}]`;
|
|
39
|
+
return `${state} ${t.Name.padEnd(40).slice(0, 40)} ${t.LastRun.padEnd(16)} ${t.NextRun.padEnd(16)} ${t.Triggers}`;
|
|
40
|
+
});
|
|
41
|
+
const header = `State ${'Name'.padEnd(40)} ${'Last Run'.padEnd(16)} ${'Next Run'.padEnd(16)} Triggers`;
|
|
42
|
+
return {
|
|
43
|
+
content: [{ type: 'text', text: `${header}\n${'─'.repeat(110)}\n${lines.join('\n')}\n\n${tasks.length} tasks` }],
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
server.tool('windows_task_scheduler_manage', 'Create, delete, enable, disable, or run a scheduled task.', {
|
|
47
|
+
action: z.enum(['create', 'delete', 'enable', 'disable', 'run']).describe('Action to perform'),
|
|
48
|
+
name: z.string().describe('Task name'),
|
|
49
|
+
command: z.string().optional().describe('Command to execute (for create)'),
|
|
50
|
+
arguments: z.string().optional().describe('Command arguments (for create)'),
|
|
51
|
+
trigger: z.enum(['once', 'daily', 'weekly', 'hourly', 'logon', 'startup']).optional().describe('Trigger type (for create)'),
|
|
52
|
+
time: z.string().optional().describe('Time for trigger as HH:mm (for create with once/daily/weekly)'),
|
|
53
|
+
interval: z.number().optional().describe('Repetition interval in minutes (for create with hourly)'),
|
|
54
|
+
}, async ({ action, name, command, arguments: args, trigger, time, interval }) => {
|
|
55
|
+
let ps;
|
|
56
|
+
switch (action) {
|
|
57
|
+
case 'run':
|
|
58
|
+
ps = `Start-ScheduledTask -TaskName '${name.replace(/'/g, "''")}' -ErrorAction Stop; "Task '${name}' started"`;
|
|
59
|
+
break;
|
|
60
|
+
case 'enable':
|
|
61
|
+
ps = `Enable-ScheduledTask -TaskName '${name.replace(/'/g, "''")}' -ErrorAction Stop | Select-Object TaskName,State | ConvertTo-Json -Compress`;
|
|
62
|
+
break;
|
|
63
|
+
case 'disable':
|
|
64
|
+
ps = `Disable-ScheduledTask -TaskName '${name.replace(/'/g, "''")}' -ErrorAction Stop | Select-Object TaskName,State | ConvertTo-Json -Compress`;
|
|
65
|
+
break;
|
|
66
|
+
case 'delete':
|
|
67
|
+
ps = `Unregister-ScheduledTask -TaskName '${name.replace(/'/g, "''")}' -Confirm:$false -ErrorAction Stop; "Task '${name}' deleted"`;
|
|
68
|
+
break;
|
|
69
|
+
case 'create': {
|
|
70
|
+
if (!command) {
|
|
71
|
+
return { content: [{ type: 'text', text: 'Create requires command.' }], isError: true };
|
|
72
|
+
}
|
|
73
|
+
if (!trigger) {
|
|
74
|
+
return { content: [{ type: 'text', text: 'Create requires trigger type.' }], isError: true };
|
|
75
|
+
}
|
|
76
|
+
const actionPart = args
|
|
77
|
+
? `$action = New-ScheduledTaskAction -Execute '${command.replace(/'/g, "''")}' -Argument '${args.replace(/'/g, "''")}'`
|
|
78
|
+
: `$action = New-ScheduledTaskAction -Execute '${command.replace(/'/g, "''")}'`;
|
|
79
|
+
let triggerPart;
|
|
80
|
+
switch (trigger) {
|
|
81
|
+
case 'once':
|
|
82
|
+
triggerPart = `$trigger = New-ScheduledTaskTrigger -Once -At '${time || '00:00'}'`;
|
|
83
|
+
break;
|
|
84
|
+
case 'daily':
|
|
85
|
+
triggerPart = `$trigger = New-ScheduledTaskTrigger -Daily -At '${time || '00:00'}'`;
|
|
86
|
+
break;
|
|
87
|
+
case 'weekly':
|
|
88
|
+
triggerPart = `$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -At '${time || '00:00'}'`;
|
|
89
|
+
break;
|
|
90
|
+
case 'hourly':
|
|
91
|
+
triggerPart = `$trigger = New-ScheduledTaskTrigger -Once -At '00:00' -RepetitionInterval (New-TimeSpan -Minutes ${interval || 60}) -RepetitionDuration (New-TimeSpan -Days 9999)`;
|
|
92
|
+
break;
|
|
93
|
+
case 'logon':
|
|
94
|
+
triggerPart = `$trigger = New-ScheduledTaskTrigger -AtLogOn`;
|
|
95
|
+
break;
|
|
96
|
+
case 'startup':
|
|
97
|
+
triggerPart = `$trigger = New-ScheduledTaskTrigger -AtStartup`;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
ps = `
|
|
101
|
+
${actionPart}
|
|
102
|
+
${triggerPart}
|
|
103
|
+
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
|
|
104
|
+
Register-ScheduledTask -TaskName '${name.replace(/'/g, "''")}' -Action $action -Trigger $trigger -Settings $settings -Force -ErrorAction Stop |
|
|
105
|
+
Select-Object TaskName,State | ConvertTo-Json -Compress`;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const result = await runPowerShell(ps, { timeout: 15000 });
|
|
110
|
+
if (result.exitCode !== 0) {
|
|
111
|
+
return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
|
|
112
|
+
}
|
|
113
|
+
return { content: [{ type: 'text', text: result.stdout }] };
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
2
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
*
|
|
4
|
+
* Tools: windows_service_list (#4), windows_service_control (#5)
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { runPowerShell } from '../shell.js';
|
|
8
|
+
export function registerServiceTools(server) {
|
|
9
|
+
server.tool('windows_service_list', 'List Windows services with status, startup type, and description.', {
|
|
10
|
+
filter: z.string().optional().describe('Filter by service name or display name (substring)'),
|
|
11
|
+
status: z.enum(['running', 'stopped', 'all']).default('all').describe('Filter by status'),
|
|
12
|
+
}, async ({ filter, status }) => {
|
|
13
|
+
const filterClause = filter
|
|
14
|
+
? `| Where-Object { $_.Name -like '*${filter.replace(/'/g, "''")}*' -or $_.DisplayName -like '*${filter.replace(/'/g, "''")}*' }`
|
|
15
|
+
: '';
|
|
16
|
+
const statusClause = status === 'running'
|
|
17
|
+
? `| Where-Object { $_.Status -eq 'Running' }`
|
|
18
|
+
: status === 'stopped'
|
|
19
|
+
? `| Where-Object { $_.Status -eq 'Stopped' }`
|
|
20
|
+
: '';
|
|
21
|
+
const ps = `
|
|
22
|
+
$wmiCache = @{}
|
|
23
|
+
Get-CimInstance Win32_Service -ErrorAction SilentlyContinue | ForEach-Object { $wmiCache[$_.Name] = $_.Description }
|
|
24
|
+
Get-Service ${filterClause} ${statusClause} | Sort-Object DisplayName | ForEach-Object {
|
|
25
|
+
[PSCustomObject]@{
|
|
26
|
+
Name = $_.Name
|
|
27
|
+
DisplayName = $_.DisplayName
|
|
28
|
+
Status = $_.Status.ToString()
|
|
29
|
+
StartType = $_.StartType.ToString()
|
|
30
|
+
Description = if ($wmiCache[$_.Name]) { $wmiCache[$_.Name] } else { '' }
|
|
31
|
+
}
|
|
32
|
+
} | ConvertTo-Json -Depth 3 -Compress`;
|
|
33
|
+
const result = await runPowerShell(ps, { timeout: 45000 });
|
|
34
|
+
if (result.exitCode !== 0) {
|
|
35
|
+
return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
|
|
36
|
+
}
|
|
37
|
+
if (!result.stdout) {
|
|
38
|
+
return { content: [{ type: 'text', text: 'No services found.' }] };
|
|
39
|
+
}
|
|
40
|
+
const services = Array.isArray(JSON.parse(result.stdout)) ? JSON.parse(result.stdout) : [JSON.parse(result.stdout)];
|
|
41
|
+
const lines = services.map((s) => `${s.Status === 'Running' ? '[RUN]' : '[STP]'} ${s.StartType.padEnd(10)} ${s.Name.padEnd(35).slice(0, 35)} ${s.DisplayName}`);
|
|
42
|
+
const header = `${'State'.padEnd(5)} ${'Startup'.padEnd(10)} ${'Name'.padEnd(35)} Display Name`;
|
|
43
|
+
return {
|
|
44
|
+
content: [{ type: 'text', text: `${header}\n${'─'.repeat(100)}\n${lines.join('\n')}\n\n${services.length} services` }],
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
server.tool('windows_service_control', 'Start, stop, restart, or change startup type of a Windows service.', {
|
|
48
|
+
name: z.string().describe('Service name or display name'),
|
|
49
|
+
action: z.enum(['start', 'stop', 'restart', 'enable', 'disable']).describe('Action to perform'),
|
|
50
|
+
}, async ({ name, action }) => {
|
|
51
|
+
let ps;
|
|
52
|
+
switch (action) {
|
|
53
|
+
case 'start':
|
|
54
|
+
ps = `Start-Service -Name '${name.replace(/'/g, "''")}' -ErrorAction Stop; Get-Service -Name '${name.replace(/'/g, "''")}' | Select-Object Name,Status,StartType | ConvertTo-Json -Compress`;
|
|
55
|
+
break;
|
|
56
|
+
case 'stop':
|
|
57
|
+
ps = `Stop-Service -Name '${name.replace(/'/g, "''")}' -Force -ErrorAction Stop; Get-Service -Name '${name.replace(/'/g, "''")}' | Select-Object Name,Status,StartType | ConvertTo-Json -Compress`;
|
|
58
|
+
break;
|
|
59
|
+
case 'restart':
|
|
60
|
+
ps = `Restart-Service -Name '${name.replace(/'/g, "''")}' -Force -ErrorAction Stop; Get-Service -Name '${name.replace(/'/g, "''")}' | Select-Object Name,Status,StartType | ConvertTo-Json -Compress`;
|
|
61
|
+
break;
|
|
62
|
+
case 'enable':
|
|
63
|
+
ps = `Set-Service -Name '${name.replace(/'/g, "''")}' -StartupType Automatic -ErrorAction Stop; Get-Service -Name '${name.replace(/'/g, "''")}' | Select-Object Name,Status,StartType | ConvertTo-Json -Compress`;
|
|
64
|
+
break;
|
|
65
|
+
case 'disable':
|
|
66
|
+
ps = `Set-Service -Name '${name.replace(/'/g, "''")}' -StartupType Disabled -ErrorAction Stop; Get-Service -Name '${name.replace(/'/g, "''")}' | Select-Object Name,Status,StartType | ConvertTo-Json -Compress`;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
const result = await runPowerShell(ps, { timeout: 15000 });
|
|
70
|
+
if (result.exitCode !== 0) {
|
|
71
|
+
return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
|
|
72
|
+
}
|
|
73
|
+
const svc = JSON.parse(result.stdout);
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: 'text', text: `Service "${svc.Name}": ${action} → Status: ${svc.Status}, StartType: ${svc.StartType}` }],
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=service.js.map
|