local-commander-agent 0.3.2 → 0.3.4
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 +87 -15
- package/bin/localcommander.mjs +72 -9
- package/package.json +8 -3
- package/runtime/JobHost.exe +0 -0
- package/runtime/LocalCommanderAgent.exe +0 -0
- package/runtime/OwnerBridge.exe +0 -0
- package/runtime/agent.mjs +715 -480
package/README.md
CHANGED
|
@@ -1,18 +1,46 @@
|
|
|
1
1
|
# Local Commander Agent
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Connect your AI assistant to your Windows computer through [Local Commander](https://localcmd.app).** The agent lets an authorized MCP client work with local files, run explicitly approved commands, and access approved localhost services on your paired computer.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> **Requires the Local Commander service at https://localcmd.app.** This npm package is its Windows companion, not a standalone MCP server, an offline AI assistant, or a JavaScript SDK. Installing the package alone is not enough: you need a Local Commander account, an approved device pairing, a running agent, an internet connection, and an authorized MCP connection from your AI client. Local management commands such as status and stop do not require the service to be online.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
[Website and sign-in](https://localcmd.app) · [Support](https://localcmd.app/support) · [Privacy](https://localcmd.app/privacy)
|
|
8
|
+
|
|
9
|
+
## Before you start
|
|
10
|
+
|
|
11
|
+
Use **Windows 11 x64** and a currently supported Node.js LTS version meeting the package's Node.js 20+ minimum. Node.js/npm are needed to launch `npx`; the installed background agent includes its own Node runtime. You do not need Docker, WSL, a repository checkout, a database, or administrator rights for normal setup. Your AI client must support authenticated remote MCP connections; availability depends on the client and workspace policy.
|
|
12
|
+
|
|
13
|
+
## Install and connect
|
|
14
|
+
|
|
15
|
+
1. Open PowerShell or Windows Terminal as your normal Windows user and run:
|
|
16
|
+
|
|
17
|
+
```powershell
|
|
18
|
+
npx local-commander-agent@latest
|
|
19
|
+
```
|
|
10
20
|
|
|
11
|
-
The
|
|
21
|
+
2. The installer opens a browser. Sign in to [Local Commander](https://localcmd.app), check the displayed pairing code, and approve this computer. Approve only a pairing you just started yourself.
|
|
22
|
+
3. Choose filesystem access in the local Windows dialog:
|
|
23
|
+
- **All connected drives — read and write:** approve all currently available drive letters shown in the dialog at once. There is no folder picker or separate approval for each drive.
|
|
24
|
+
- **Selected folders:** choose one folder in the folder picker. It starts read/write; more folders and write permission can be added later.
|
|
12
25
|
|
|
13
|
-
|
|
26
|
+
Cancel leaves existing permissions unchanged. On first setup, cancellation stops before enabling autostart or starting the agent. Existing grants are preserved when repeating installation or updating: an update never opts you into all drives.
|
|
27
|
+
4. After the terminal reports that Local Commander is installed and running, connect your AI client to the remote MCP endpoint:
|
|
14
28
|
|
|
15
|
-
|
|
29
|
+
```text
|
|
30
|
+
https://localcmd.app/mcp
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Complete the browser authorization with the same Local Commander account. Device pairing and AI-client authorization are separate steps. This package does not expose a local stdio MCP server, so do not configure it as `command: npx` in an AI client's stdio-server settings.
|
|
34
|
+
|
|
35
|
+
You can close the installation terminal. The agent runs in the background and starts when this Windows user signs in. The PC must be awake and online for remote AI tasks. As a first test, ask the connected assistant to list the files in a folder you approved.
|
|
36
|
+
|
|
37
|
+
## What “all connected drives” means
|
|
38
|
+
|
|
39
|
+
It expands the **filesystem scope** to the drive letters shown at the moment you consent. It includes available local fixed and removable drives. Mapped network drives and `subst` letters are not included. New drive letters, unavailable drives and unmounted volumes are not automatically added; run `configure-access` again after making the desired drive available. Grants are path/drive-letter based, not tied to a particular removable disk's hardware identity: reusing an approved letter gives access under that letter.
|
|
40
|
+
|
|
41
|
+
This is not administrator access or an unrestricted shell. Windows permissions, protected Local Commander state/runtime and secret-file filters still apply to file tools. Direct command execution and localhost ports remain separately controlled. Read/write access is powerful: an assistant may read personal documents or change accessible files, and writing startup items, scripts or Git hooks can make programs run later as you. Treat drive-wide write access like permission to run programs. Folder controls are not an OS sandbox for an executable you separately allow. ChatGPT/Claude or your workspace may still ask for their own action confirmations.
|
|
42
|
+
|
|
43
|
+
## Manage the installation
|
|
16
44
|
|
|
17
45
|
```powershell
|
|
18
46
|
npx local-commander-agent@latest status
|
|
@@ -20,25 +48,69 @@ npx local-commander-agent@latest start
|
|
|
20
48
|
npx local-commander-agent@latest stop
|
|
21
49
|
npx local-commander-agent@latest restart
|
|
22
50
|
npx local-commander-agent@latest update
|
|
23
|
-
npx local-commander-agent@latest approve-workspace
|
|
24
51
|
npx local-commander-agent@latest uninstall
|
|
25
52
|
```
|
|
26
53
|
|
|
27
|
-
|
|
54
|
+
`status` reports the local installed version, process and pairing state; it is not proof of a healthy cloud relay connection. Installation files and per-user state live under `%LOCALAPPDATA%\LocalCommander`. Updating preserves enrollment and permissions. Close or finish active AI tasks before restarting or updating.
|
|
55
|
+
|
|
56
|
+
Normal uninstall preserves protected enrollment state for reinstallation. To also remove this computer's local enrollment and permissions:
|
|
28
57
|
|
|
29
58
|
```powershell
|
|
30
59
|
npx local-commander-agent@latest uninstall --purge-state
|
|
31
60
|
```
|
|
32
61
|
|
|
33
|
-
Uninstall never
|
|
62
|
+
Uninstall never deletes your project folders. For a lost or untrusted computer, also revoke its access through your Local Commander account; removing local files is not a substitute for cloud revocation.
|
|
63
|
+
|
|
64
|
+
## Change access later
|
|
65
|
+
|
|
66
|
+
Run these commands yourself on the paired computer. Install the release containing these commands with `update` before using them on an older installation.
|
|
67
|
+
|
|
68
|
+
```powershell
|
|
69
|
+
npx local-commander-agent@latest configure-access
|
|
70
|
+
npx local-commander-agent@latest approve-workspace
|
|
71
|
+
npx local-commander-agent@latest workspaces
|
|
72
|
+
npx local-commander-agent@latest revoke-workspace <workspaceGrantId>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`configure-access` reopens the all-drives/selected-folder choice. **It replaces the current set of filesystem grants and clears their command and localhost permissions.** Choosing a restricted folder actually removes the old broader grants. `approve-workspace` adds another read/write folder without replacing the existing set. `workspaces` shows the grant IDs and permissions to use with the other commands. A successful change restarts an already-running agent to synchronize grants; cancellation does not restart it. A revoked drive is not silently re-added in the background.
|
|
76
|
+
|
|
77
|
+
To enable or remove individual capabilities, replace the placeholders with a grant ID from `workspaces` and an executable path that actually exists on your PC:
|
|
78
|
+
|
|
79
|
+
```powershell
|
|
80
|
+
npx local-commander-agent@latest allow-write <workspaceGrantId>
|
|
81
|
+
npx local-commander-agent@latest allow-command <workspaceGrantId> node "C:\Program Files\nodejs\node.exe"
|
|
82
|
+
npx local-commander-agent@latest allow-localhost <workspaceGrantId> 3000
|
|
83
|
+
|
|
84
|
+
npx local-commander-agent@latest deny-write <workspaceGrantId>
|
|
85
|
+
npx local-commander-agent@latest deny-command <workspaceGrantId> node
|
|
86
|
+
npx local-commander-agent@latest deny-localhost <workspaceGrantId> 3000
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`allow-command` associates an exact executable with the name the assistant uses; it does not accept a shell command string. Capability changes are local owner actions, not MCP settings the assistant can silently widen. The all-drives decision always requires the local dialog; there is no `--yes` or environment flag to approve it.
|
|
90
|
+
|
|
91
|
+
## Troubleshooting
|
|
92
|
+
|
|
93
|
+
| Symptom | What to check |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| `npx` is not recognized | Install supported Node.js LTS, then open a new terminal. |
|
|
96
|
+
| Unsupported OS or architecture | This package targets Windows x64, not macOS, Linux or Windows ARM64. |
|
|
97
|
+
| Pairing does not complete | Open https://localcmd.app, verify your account and internet access, then rerun the install command to resume. Do not share pairing codes. |
|
|
98
|
+
| `ACCESS_SETUP_CANCELLED` | No filesystem grant was saved by that dialog. Rerun setup and choose the intended scope. |
|
|
99
|
+
| `STATE_CHANGED` | Local account/policy/permissions changed while the dialog was open. Review current state and reopen the dialog. |
|
|
100
|
+
| `WORKSPACE_INVALID` | Choose an existing ordinary folder, not a link or protected state directory. Use the all-drives choice for drive roots. |
|
|
101
|
+
| `LOCAL_PERMISSION_REQUIRED` | Inspect `workspaces`; enable only the exact write/command/localhost capability needed. |
|
|
102
|
+
| `PATH_NOT_ALLOWED` | The target is outside the grant, resolves elsewhere through a link, or is protected. Do not bypass the boundary with another tool. |
|
|
103
|
+
| Paired and running, but the AI cannot reach the PC | Check that the PC is awake/online and that the AI client's remote MCP connection is authorized to the same account. Check service status/support. |
|
|
104
|
+
|
|
105
|
+
For help, use [Local Commander support](https://localcmd.app/support) or email [peter.mamrukov@localcmd.app](mailto:peter.mamrukov@localcmd.app). Share error codes and the installed version, not keys, tokens, pairing codes, private state files or unredacted logs.
|
|
34
106
|
|
|
35
|
-
##
|
|
107
|
+
## Operator-only: a different control plane
|
|
36
108
|
|
|
37
|
-
|
|
109
|
+
Normal customers should not change the service origin. An operator testing a compatible staging or self-hosted **complete Local Commander service** may set the origin before first pairing:
|
|
38
110
|
|
|
39
111
|
```powershell
|
|
40
|
-
$env:LC_CONTROL_PLANE_ORIGIN="https://your-control-plane.example"
|
|
112
|
+
$env:LC_CONTROL_PLANE_ORIGIN = "https://your-control-plane.example"
|
|
41
113
|
npx local-commander-agent@latest
|
|
42
114
|
```
|
|
43
115
|
|
|
44
|
-
|
|
116
|
+
Enrollment retains the chosen origin. Changing this variable does not move an already paired device or turn the package into a standalone MCP server. Account/device enrollment, OAuth, the control plane and relay are still required.
|
package/bin/localcommander.mjs
CHANGED
|
@@ -13,7 +13,7 @@ const DEFAULT_ORIGIN = String(PACKAGE_META.localCommander?.controlPlaneOrigin ||
|
|
|
13
13
|
const RUNTIME_FILES = ["LocalCommanderAgent.exe", "OwnerBridge.exe", "JobHost.exe", "agent.mjs", "node.exe", "NODE-LICENSE.txt"];
|
|
14
14
|
const RUN_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
|
15
15
|
const RUN_VALUE = "LocalCommanderAgent";
|
|
16
|
-
const MAX_STDIO =
|
|
16
|
+
const MAX_STDIO = 1024 * 1024;
|
|
17
17
|
|
|
18
18
|
function fail(code) {
|
|
19
19
|
const error = new Error(code);
|
|
@@ -231,10 +231,47 @@ async function pair(runtimeDir) {
|
|
|
231
231
|
if (!opened) fail("PAIRING_RESPONSE_INVALID");
|
|
232
232
|
process.stdout.write("Device paired.\n");
|
|
233
233
|
}
|
|
234
|
+
async function configureAccess(runtimeDir) {
|
|
235
|
+
const result = execAgent(runtimeDir, ["configure-access"], 300_000);
|
|
236
|
+
if (result.status !== 0) fail(result.stderr.trim() || "ACCESS_CONFIGURATION_FAILED");
|
|
237
|
+
const answer = result.stdout.trim();
|
|
238
|
+
if (answer === "ACCESS_CANCELLED") return false;
|
|
239
|
+
if (answer !== "ACCESS_CONFIGURED") fail("ACCESS_CONFIGURATION_INVALID");
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
async function restartIfRunning() {
|
|
243
|
+
const record = await readProcessRecord();
|
|
244
|
+
if (record && pidAlive(record.pid)) { await stopAgent(); await startAgent(); }
|
|
245
|
+
}
|
|
234
246
|
async function approveWorkspace(runtimeDir) {
|
|
235
247
|
const result = execAgent(runtimeDir, ["approve-workspace"], 180_000);
|
|
236
248
|
if (result.status !== 0) fail(result.stderr.trim() || "WORKSPACE_APPROVAL_FAILED");
|
|
237
|
-
|
|
249
|
+
const answer = result.stdout.trim();
|
|
250
|
+
if (answer === "WORKSPACE_CANCELLED") return false;
|
|
251
|
+
if (answer !== "WORKSPACE_APPROVED") fail("WORKSPACE_APPROVAL_INVALID");
|
|
252
|
+
process.stdout.write("Workspace approved.\n");
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
// Owner-only local permission surface. These run the installed runtime's own
|
|
256
|
+
// commands on this PC; the cloud, the AI client and MCP calls cannot reach them.
|
|
257
|
+
const PERMISSION_COMMANDS = { "revoke-workspace": 1, "allow-write": 1, "deny-write": 1, "allow-command": 3, "deny-command": 2, "allow-localhost": 2, "deny-localhost": 2 };
|
|
258
|
+
async function listWorkspaces(runtimeDir) {
|
|
259
|
+
const result = execAgent(runtimeDir, ["list-workspaces"], 15_000);
|
|
260
|
+
if (result.status !== 0) fail(result.stderr.trim() || "WORKSPACE_LIST_FAILED");
|
|
261
|
+
try { process.stdout.write(JSON.stringify(JSON.parse(result.stdout.trim() || "[]"), null, 2) + "\n"); }
|
|
262
|
+
catch { fail("WORKSPACE_LIST_INVALID"); }
|
|
263
|
+
}
|
|
264
|
+
async function changePermission(runtimeDir, command, args) {
|
|
265
|
+
if (args.length !== PERMISSION_COMMANDS[command]) fail("ARGUMENT_INVALID");
|
|
266
|
+
// Covers the 60 s native owner review plus DPAPI/state work (see planning/OWNER_PERMISSIONS.md).
|
|
267
|
+
const result = execAgent(runtimeDir, [command, ...args], 90_000);
|
|
268
|
+
if (result.status !== 0) fail(result.stderr.trim() || "PERMISSION_UPDATE_FAILED");
|
|
269
|
+
const record = await readProcessRecord();
|
|
270
|
+
if (record && pidAlive(record.pid)) {
|
|
271
|
+
await stopAgent();
|
|
272
|
+
await startAgent();
|
|
273
|
+
process.stdout.write("Permission updated; Local Commander restarted.\n");
|
|
274
|
+
} else process.stdout.write("Permission updated.\n");
|
|
238
275
|
}
|
|
239
276
|
function pidAlive(pid) {
|
|
240
277
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -319,11 +356,11 @@ async function bootstrap({ chooseWorkspace = true } = {}) {
|
|
|
319
356
|
if (!identity.paired) await pair(runtimeDir);
|
|
320
357
|
if (chooseWorkspace) {
|
|
321
358
|
const list = execAgent(runtimeDir, ["list-workspaces"], 15_000);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (
|
|
359
|
+
if (list.status !== 0) fail("WORKSPACE_LIST_FAILED");
|
|
360
|
+
let existing;
|
|
361
|
+
try { existing = JSON.parse(list.stdout); } catch { fail("WORKSPACE_LIST_INVALID"); }
|
|
362
|
+
if (!Array.isArray(existing)) fail("WORKSPACE_LIST_INVALID");
|
|
363
|
+
if (existing.length === 0 && !(await configureAccess(runtimeDir))) fail("ACCESS_SETUP_CANCELLED");
|
|
327
364
|
}
|
|
328
365
|
registerAutostart(runtimeDir);
|
|
329
366
|
await startAgent();
|
|
@@ -355,7 +392,14 @@ function help() {
|
|
|
355
392
|
" npx local-commander-agent@latest stop stop background agent",
|
|
356
393
|
" npx local-commander-agent@latest restart restart background agent",
|
|
357
394
|
" npx local-commander-agent@latest update install this package version and restart",
|
|
358
|
-
" npx local-commander-agent@latest
|
|
395
|
+
" npx local-commander-agent@latest configure-access choose all connected drives or selected folders",
|
|
396
|
+
" npx local-commander-agent@latest approve-workspace add another read/write folder",
|
|
397
|
+
" npx local-commander-agent@latest revoke-workspace <workspaceGrantId>",
|
|
398
|
+
" npx local-commander-agent@latest workspaces list approved workspaces and permissions",
|
|
399
|
+
" npx local-commander-agent@latest allow-write <workspaceGrantId>",
|
|
400
|
+
" npx local-commander-agent@latest allow-command <workspaceGrantId> <name> <absolute-executable-path>",
|
|
401
|
+
" npx local-commander-agent@latest allow-localhost <workspaceGrantId> <port>",
|
|
402
|
+
" npx local-commander-agent@latest deny-write|deny-command|deny-localhost ...",
|
|
359
403
|
" npx local-commander-agent@latest uninstall [--purge-state]",
|
|
360
404
|
""
|
|
361
405
|
].join("\n");
|
|
@@ -403,11 +447,30 @@ async function main() {
|
|
|
403
447
|
process.stdout.write(JSON.stringify(await statusObject(), null, 2) + "\n");
|
|
404
448
|
return;
|
|
405
449
|
}
|
|
450
|
+
if (command === "configure-access") {
|
|
451
|
+
if (args.length) fail("ARGUMENT_INVALID");
|
|
452
|
+
const current = await readCurrent();
|
|
453
|
+
if (!current) fail("AGENT_NOT_INSTALLED");
|
|
454
|
+
if (await configureAccess(current.runtimeDir)) {
|
|
455
|
+
await restartIfRunning();
|
|
456
|
+
process.stdout.write("Filesystem access configured.\n");
|
|
457
|
+
} else process.stdout.write("Access selection cancelled; existing permissions were preserved.\n");
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
406
460
|
if (command === "approve-workspace") {
|
|
407
461
|
if (args.length) fail("ARGUMENT_INVALID");
|
|
408
462
|
const current = await readCurrent();
|
|
409
463
|
if (!current) fail("AGENT_NOT_INSTALLED");
|
|
410
|
-
await approveWorkspace(current.runtimeDir);
|
|
464
|
+
if (await approveWorkspace(current.runtimeDir)) await restartIfRunning();
|
|
465
|
+
else process.stdout.write("Workspace selection cancelled.\n");
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (command === "workspaces" || Object.hasOwn(PERMISSION_COMMANDS, command)) {
|
|
469
|
+
if (command === "workspaces" && args.length) fail("ARGUMENT_INVALID");
|
|
470
|
+
const current = await readCurrent();
|
|
471
|
+
if (!current) fail("AGENT_NOT_INSTALLED");
|
|
472
|
+
if (command === "workspaces") await listWorkspaces(current.runtimeDir);
|
|
473
|
+
else await changePermission(current.runtimeDir, command, args);
|
|
411
474
|
return;
|
|
412
475
|
}
|
|
413
476
|
if (command === "uninstall") {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-commander-agent",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.4",
|
|
4
|
+
"description": "Windows companion for Local Commander: connect AI clients to local files, approved commands and localhost. Requires https://localcmd.app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"localcommander": "bin/localcommander.mjs"
|
|
@@ -36,6 +36,11 @@
|
|
|
36
36
|
"windows"
|
|
37
37
|
],
|
|
38
38
|
"localCommander": {
|
|
39
|
-
"controlPlaneOrigin": "https://localcmd.
|
|
39
|
+
"controlPlaneOrigin": "https://localcmd.app"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://localcmd.app",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://localcmd.app/support",
|
|
44
|
+
"email": "peter.mamrukov@localcmd.app"
|
|
40
45
|
}
|
|
41
46
|
}
|
package/runtime/JobHost.exe
CHANGED
|
Binary file
|
|
Binary file
|
package/runtime/OwnerBridge.exe
CHANGED
|
Binary file
|