local-commander-agent 0.3.17-journal.1 → 0.3.22-fullaccess.1
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 +21 -8
- package/README.md +7 -6
- package/bin/command-path.mjs +1 -1
- package/bin/monitor-agents.mjs +12 -12
- package/bin/monitor-savings.d.mts +20 -0
- package/bin/monitor-savings.mjs +71 -0
- package/bin/monitor.mjs +7 -3
- package/package.json +1 -1
- package/runtime/JobHost.exe +0 -0
- package/runtime/LocalCommanderAgent.exe +0 -0
- package/runtime/OwnerBridge.exe +0 -0
- package/runtime/agent.mjs +533 -399
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
# Change history
|
|
2
|
-
|
|
3
|
-
## 0.3.
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
|
|
1
|
+
# Change history
|
|
2
|
+
|
|
3
|
+
## 0.3.22-fullaccess.1 - 2026-10-10
|
|
4
|
+
|
|
5
|
+
- Owner-approved Full Access no longer requires per-executable registration for Windows shells or administration tools. The Windows account rights and explicit deny-command restrictions remain enforced.
|
|
6
|
+
- Scoped grants and native authentication, target binding, journal/recovery and concurrency safeguards are unchanged.
|
|
7
|
+
- Local direct MCP and remote startup improvements remain separate issue #104 tasks; this build still uses authenticated hosted relay for ChatGPT web.
|
|
8
|
+
|
|
9
|
+
## 0.3.17-journal.1 - 2026-10-08
|
|
10
|
+
|
|
11
|
+
- Apply existing retention maintenance when a new request reaches the journal entry limit on a long-lived connection.
|
|
12
|
+
- Remove only expired, acknowledged terminal records under the existing integrity and retention checks.
|
|
13
|
+
- Keep active, unknown, unacknowledged and recent records protected; admission still fails closed when no record is eligible.
|
|
14
|
+
|
|
9
15
|
Includes the aligned monitor and expanded names from 0.3.16. This does not release unknown cloud reservations or replay work.
|
|
10
16
|
|
|
11
17
|
## 0.3.16-monitor.1 — 2026-10-08
|
|
@@ -24,3 +30,10 @@ Names are display hints. IDs and permissions are unchanged. Token figures remain
|
|
|
24
30
|
- Release cloud reservations only after a rejection receipt passes the existing execution and device checks.
|
|
25
31
|
- Retain fencing for unknown executions without rejection proof.
|
|
26
32
|
- Prune acknowledged terminal journal records using the existing retention rules.
|
|
33
|
+
# 0.3.19-admission.1
|
|
34
|
+
|
|
35
|
+
New execution admission pauses when protected journal capacity is exhausted; authenticated relay status carries the pause while status, output and cancellation remain available. Retains the verified native18 default of 2,000 entries, the existing byte/retention limits and unknown-execution fencing.
|
|
36
|
+
|
|
37
|
+
# 0.3.21-project-targets.3
|
|
38
|
+
|
|
39
|
+
Explicit project targets use on-demand repository identity and constrain session paths to the selected repository. Retains installed target20 behavior and adds accepted journal admission guards and authenticated readiness reporting; existing journal retention, permission checks and unknown-execution fencing remain.
|
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ npx local-commander-agent@latest workspaces
|
|
|
74
74
|
npx local-commander-agent@latest revoke-workspace <workspaceGrantId>
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
`configure-access` reopens the full-access/selected-folder choice. **It replaces current grants and their restrictions.** Full access enables commands and localhost by default; selected folders start read/write with separate command/port allowlists. Choosing a restricted folder removes old broader grants. `approve-workspace` adds another read/write folder without replacing the existing set. `workspaces` shows grant IDs, full/scoped mode and explicit denials. 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.
|
|
77
|
+
`configure-access` reopens the full-access/selected-folder choice. **It replaces the current set of grants and their restrictions.** Full access enables commands and localhost by default; selected folders start read/write with separate command/port allowlists. Choosing a restricted folder removes old broader grants. `approve-workspace` adds another read/write folder without replacing the existing set. `workspaces` shows grant IDs, full/scoped mode and explicit denials. 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.
|
|
78
78
|
|
|
79
79
|
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:
|
|
80
80
|
|
|
@@ -90,13 +90,13 @@ npx local-commander-agent@latest deny-localhost <workspaceGrantId> 3000
|
|
|
90
90
|
|
|
91
91
|
`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.
|
|
92
92
|
|
|
93
|
-
###
|
|
93
|
+
### Scoped access: approve Git from CMD or PowerShell
|
|
94
94
|
|
|
95
95
|
When giving an owner Windows commands, identify the shell before using shell-specific syntax and label the example. Prefer explicit workspace IDs and quoted absolute executable paths that work in both CMD and PowerShell; do not assume a standard installation path.
|
|
96
96
|
|
|
97
97
|
Use the example for the shell you are running. A prompt such as `C:\project>` is normally **Command Prompt (CMD)**; `PS C:\project>` is normally **PowerShell**. Windows Terminal can host either shell.
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
For selected-folders/scoped mode, first run `npx local-commander-agent@latest workspaces` and copy the intended workspace's grant ID. Full Access does not require this setup for Git.
|
|
100
100
|
|
|
101
101
|
**CMD:** run `where git.exe`. If it lists several paths, choose the Git executable you intend to approve. Replace `WORKSPACE_ID` and the example path below with those actual values; keep the path in double quotes.
|
|
102
102
|
|
|
@@ -118,9 +118,10 @@ Do not paste `(Get-Command git).Source` into CMD: CMD passes it literally instea
|
|
|
118
118
|
|
|
119
119
|
Under full access, `deny-command` and `deny-localhost` exclude the named command or port while other commands/ports remain available. Native `.exe`/`.com` commands receive argument arrays directly. `.cmd`/`.bat` launchers use a hidden PowerShell wrapper and reject cmd metacharacters in arguments; use an explicit interpreter for more complex scripts. Allowing a general interpreter is not containment against it launching a denied program.
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
Under **owner-approved Full Access**, installed Windows shells, script hosts and administration executables (including PowerShell and CMD) work without per-command registration. They execute under the Windows account's own rights, not elevated privileges. Owner-defined `deny-command` exclusions and explicit write/localhost restrictions still apply. A general interpreter can access resources beyond file-tool path checks; Full Access is not an OS sandbox. Existing scoped grants stay scoped until the owner deliberately changes access with `configure-access`.
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
|
|
124
|
+
For a remote MCP client with an unknown device/workspace, use `workspace_list`, then `execution_session_open`, perform the requested work and read results. Reuse valid, already authorized session and target context where supported. `diagnostics` is for faults, not a mandatory startup roundtrip. Diagnostics reports quota separately from the device's execution slot limit. Its optional `repairSafe` mode releases only expired reservations from this OAuth connection that never acquired a dispatch lease. It never retries work, changes permissions or clears unknown executions.
|
|
124
125
|
|
|
125
126
|
## Troubleshooting
|
|
126
127
|
|
|
@@ -207,4 +208,4 @@ Reload the personal MCP server once to load this source change, then reopen `npm
|
|
|
207
208
|
|
|
208
209
|
The view shows measured per-agent calls, mean tool-plus-queue duration, stdout bytes, sent response bytes and errors when an identified reporter has those counters. Native measured calls, status/output polls and session records are separate figures; sessions are not execution capacity. Current savings totals exclude stale native snapshots. Missing measurements remain unavailable.
|
|
209
210
|
|
|
210
|
-
See [CHANGELOG.md](CHANGELOG.md) for release changes.
|
|
211
|
+
See [CHANGELOG.md](https://github.com/netbka/local-commander-mcp/blob/main/packages/agent-cli/CHANGELOG.md) for release changes.
|
package/bin/command-path.mjs
CHANGED
|
@@ -7,7 +7,7 @@ const SHIM_MARKER = "@rem Local Commander managed command\r\n";
|
|
|
7
7
|
const WRAPPER_MARKER = "# Local Commander managed command\r\n";
|
|
8
8
|
const MONITOR_WRAPPER = "localcommander-monitor.ps1";
|
|
9
9
|
const SHORTCUT_NAME = "Local Commander Monitor";
|
|
10
|
-
const CLI_FILES = ["package.json", "bin/localcommander.mjs", "bin/monitor.mjs", "bin/monitor-agents.mjs", "bin/monitor-context.mjs", "bin/monitor-mcp.mjs", "bin/command-path.mjs"];
|
|
10
|
+
const CLI_FILES = ["package.json", "bin/localcommander.mjs", "bin/monitor.mjs", "bin/monitor-agents.mjs", "bin/monitor-context.mjs", "bin/monitor-mcp.mjs", "bin/monitor-savings.mjs", "bin/command-path.mjs"];
|
|
11
11
|
|
|
12
12
|
function validVersion(version) {
|
|
13
13
|
if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(version)) throw Error("PACKAGE_VERSION_INVALID");
|
package/bin/monitor-agents.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { lstat, open } from "node:fs/promises";
|
|
4
|
-
import { lstatSync, readFileSync } from "node:fs";
|
|
3
|
+
import { lstat, open } from "node:fs/promises";
|
|
4
|
+
import { lstatSync, readFileSync } from "node:fs";
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { promisify } from "node:util";
|
|
@@ -120,16 +120,16 @@ $rows = @(Get-Process -Name codex,claude,cursor-agent -ErrorAction SilentlyConti
|
|
|
120
120
|
})
|
|
121
121
|
ConvertTo-Json -InputObject $rows -Compress`;
|
|
122
122
|
|
|
123
|
-
export function presenceDirectory(projectRoot = process.cwd()) {
|
|
124
|
-
let state = process.env.LCMCP_OAUTH_STATE_PATH;
|
|
125
|
-
if (!state) try {
|
|
126
|
-
const file = path.resolve(projectRoot, process.env.LCMCP_CONFIG?.trim() || "local-commander.config.json");
|
|
127
|
-
const stat = lstatSync(file);
|
|
128
|
-
if (stat.isFile() && !stat.isSymbolicLink() && stat.size <= 65536)
|
|
129
|
-
state = JSON.parse(readFileSync(file, "utf8").replace(/^\uFEFF/, "")).auth?.oauthStatePath;
|
|
130
|
-
} catch { /* Missing/unreadable local configuration leaves observations unavailable. */ }
|
|
131
|
-
return typeof state === "string" && state.trim() ? path.join(path.dirname(path.resolve(projectRoot, state)), ".local-commander") : path.join(projectRoot, ".local-commander");
|
|
132
|
-
}
|
|
123
|
+
export function presenceDirectory(projectRoot = process.cwd()) {
|
|
124
|
+
let state = process.env.LCMCP_OAUTH_STATE_PATH;
|
|
125
|
+
if (!state) try {
|
|
126
|
+
const file = path.resolve(projectRoot, process.env.LCMCP_CONFIG?.trim() || "local-commander.config.json");
|
|
127
|
+
const stat = lstatSync(file);
|
|
128
|
+
if (stat.isFile() && !stat.isSymbolicLink() && stat.size <= 65536)
|
|
129
|
+
state = JSON.parse(readFileSync(file, "utf8").replace(/^\uFEFF/, "")).auth?.oauthStatePath;
|
|
130
|
+
} catch { /* Missing/unreadable local configuration leaves observations unavailable. */ }
|
|
131
|
+
return typeof state === "string" && state.trim() ? path.join(path.dirname(path.resolve(projectRoot, state)), ".local-commander") : path.join(projectRoot, ".local-commander");
|
|
132
|
+
}
|
|
133
133
|
|
|
134
134
|
export async function readAgentActivity(directory, { previousHosts = new Map(), signal, projectRoot = process.cwd() } = {}) {
|
|
135
135
|
const computer = os.hostname().replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 64) || "PC";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface LifetimeSavings {
|
|
2
|
+
scope: "all_time";
|
|
3
|
+
coverage: "recorded_native_results";
|
|
4
|
+
since: string | null;
|
|
5
|
+
asOf: string | null;
|
|
6
|
+
calls: number;
|
|
7
|
+
originalBytes: number;
|
|
8
|
+
sentBytes: number;
|
|
9
|
+
savedBytes: number;
|
|
10
|
+
}
|
|
11
|
+
export function lifetimeSavingsView(raw: unknown): LifetimeSavings | null;
|
|
12
|
+
export function summarizeLifetimeSavings(result: {
|
|
13
|
+
instances?: Array<{ lifetimeSavings?: unknown; savingsSourceId?: string }>;
|
|
14
|
+
unavailable?: number;
|
|
15
|
+
}): { available: boolean; partial: boolean; unavailable: number; sources?: number;
|
|
16
|
+
calls?: number; originalBytes?: number; sentBytes?: number; savedBytes?: number;
|
|
17
|
+
since?: string | null; asOf?: string | null; estimatedSavedTokens?: number };
|
|
18
|
+
export function formatLifetimeSavings(result: Parameters<typeof summarizeLifetimeSavings>[0] & {
|
|
19
|
+
mcp?: { instances: Array<{ instanceId: string; metrics?: Array<{ savedBytes: number }> }>; unavailable?: number };
|
|
20
|
+
}): string[];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Scalar projections only. Never read a ledger, private state or provider data here.
|
|
2
|
+
const count = value => Number.isSafeInteger(value) && value >= 0 && value <= 1e15;
|
|
3
|
+
const date = value => typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value) && Number.isFinite(Date.parse(value)) && new Date(value).toISOString() === value;
|
|
4
|
+
|
|
5
|
+
/** Invalid or older optional telemetry must not hide the rest of a computer. */
|
|
6
|
+
export function lifetimeSavingsView(raw) {
|
|
7
|
+
if (!raw || raw.scope !== "all_time" || raw.coverage !== "recorded_native_results" ||
|
|
8
|
+
![raw.calls, raw.originalBytes, raw.sentBytes, raw.savedBytes].every(count) ||
|
|
9
|
+
raw.sentBytes > raw.originalBytes || raw.savedBytes !== raw.originalBytes - raw.sentBytes ||
|
|
10
|
+
(raw.calls === 0 ? raw.since !== null || raw.asOf !== null || raw.originalBytes !== 0 :
|
|
11
|
+
!date(raw.since) || !date(raw.asOf) || raw.since > raw.asOf)) return null;
|
|
12
|
+
return { scope: "all_time", coverage: "recorded_native_results", since: raw.since, asOf: raw.asOf,
|
|
13
|
+
calls: raw.calls, originalBytes: raw.originalBytes, sentBytes: raw.sentBytes, savedBytes: raw.savedBytes };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Aggregate complete scalar native histories, even when their producers have stopped. */
|
|
17
|
+
export function summarizeLifetimeSavings(result) {
|
|
18
|
+
const sources = new Map(), rejected = new Set();
|
|
19
|
+
let unavailable = result.unavailable ?? 0;
|
|
20
|
+
// Ascending counts expose old conflicts too, regardless of the reader's file order.
|
|
21
|
+
const snapshots = (result.instances ?? []).map(instance => ({
|
|
22
|
+
id: instance.savingsSourceId, value: lifetimeSavingsView(instance.lifetimeSavings)
|
|
23
|
+
})).sort((a, b) => (a.value?.calls ?? 0) - (b.value?.calls ?? 0));
|
|
24
|
+
for (const { id, value } of snapshots) {
|
|
25
|
+
if (typeof id !== "string" || !/^[a-f0-9]{64}$/.test(id)) { unavailable++; continue; }
|
|
26
|
+
if (rejected.has(id)) continue;
|
|
27
|
+
const old = sources.get(id);
|
|
28
|
+
const conflict = old && (value?.calls === old.calls ?
|
|
29
|
+
Object.keys(old).some(key => value[key] !== old[key]) :
|
|
30
|
+
value && (["originalBytes", "sentBytes", "savedBytes"].some(key => value[key] < old[key]) ||
|
|
31
|
+
(old.since !== null && value.since > old.since) || (old.asOf !== null && value.asOf < old.asOf)));
|
|
32
|
+
if (!value || conflict) {
|
|
33
|
+
sources.delete(id); rejected.add(id); unavailable++; continue;
|
|
34
|
+
}
|
|
35
|
+
sources.set(id, value);
|
|
36
|
+
}
|
|
37
|
+
const values = [...sources.values()];
|
|
38
|
+
const totals = { calls: 0, originalBytes: 0, sentBytes: 0, savedBytes: 0 };
|
|
39
|
+
for (const value of values) for (const key of Object.keys(totals)) totals[key] += value[key];
|
|
40
|
+
if (!Object.values(totals).every(count)) return { available: false, partial: true, unavailable: unavailable + 1 };
|
|
41
|
+
const dates = values.map(value => value.since).filter(Boolean).sort();
|
|
42
|
+
const updates = values.map(value => value.asOf).filter(Boolean).sort();
|
|
43
|
+
return { available: values.length > 0, partial: unavailable > 0, unavailable, sources: values.length,
|
|
44
|
+
...totals, since: dates[0] ?? null, asOf: updates.at(-1) ?? null,
|
|
45
|
+
estimatedSavedTokens: Math.round(totals.savedBytes / 4) };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Native all-time and retained personal MCP have different coverage and byte units. */
|
|
49
|
+
export function formatLifetimeSavings(result) {
|
|
50
|
+
const total = summarizeLifetimeSavings(result);
|
|
51
|
+
const tokens = bytes => "~" + Math.round(bytes / 4).toLocaleString("en-US");
|
|
52
|
+
const personal = new Map();
|
|
53
|
+
for (const instance of result.mcp?.instances ?? []) personal.set(instance.instanceId, instance);
|
|
54
|
+
const personalBytes = [...personal.values()].flatMap(instance => instance.metrics ?? [])
|
|
55
|
+
.reduce((sum, metric) => sum + metric.savedBytes, 0);
|
|
56
|
+
const personalKnown = [...personal.values()].some(instance => instance.metrics?.length) && count(personalBytes);
|
|
57
|
+
const knownBytes = (total.available ? total.savedBytes : 0) + (personalKnown ? personalBytes : 0);
|
|
58
|
+
const available = (total.available || personalKnown) && count(knownBytes);
|
|
59
|
+
const partial = total.partial || !total.available || personal.size > 0 || Boolean(result.mcp?.unavailable);
|
|
60
|
+
const lines = [available ?
|
|
61
|
+
"All-time saved" + (partial ? " (partial)" : " (native)") + ": " + tokens(knownBytes) + " tokens" :
|
|
62
|
+
"All-time saved: unavailable | historical measurements not reported"];
|
|
63
|
+
if (total.available) lines.push("Native history: Raw " + tokens(total.originalBytes) + " | Sent " + tokens(total.sentBytes) +
|
|
64
|
+
" | " + (total.originalBytes ? (100 * total.savedBytes / total.originalBytes).toFixed(1) : "0.0") + "% | " +
|
|
65
|
+
(total.since ? "since " + total.since.slice(0, 10) + " | as of " + total.asOf.slice(0, 16).replace("T", " ") + "Z" : "no recorded results"));
|
|
66
|
+
lines.push(personalKnown ?
|
|
67
|
+
"Personal MCP (partial): " + tokens(personalBytes) + " tok retained" :
|
|
68
|
+
"Personal MCP saved: unavailable (historical total not reported)");
|
|
69
|
+
lines.push("Est: bytes/4; provider tokens unknown");
|
|
70
|
+
return lines;
|
|
71
|
+
}
|
package/bin/monitor.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { lstat, open, readdir } from "node:fs/promises";
|
|
2
|
+
import { lifetimeSavingsView, formatLifetimeSavings } from "./monitor-savings.mjs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { setTimeout as delay } from "node:timers/promises";
|
|
4
5
|
import { readAgentActivity } from "./monitor-agents.mjs";
|
|
@@ -99,7 +100,9 @@ export function monitorView(raw, now = Date.now(), alive = pidAlive) {
|
|
|
99
100
|
});
|
|
100
101
|
return { instanceId: raw.instanceId, pid: raw.pid, agentVersion: raw.agentVersion, updatedAt: raw.updatedAt,
|
|
101
102
|
running, connection, origin, sessions, events, computer: raw.computer ?? null, processName: "lc-agent", executions,
|
|
102
|
-
efficiency: efficiencyView(raw.efficiency)
|
|
103
|
+
efficiency: efficiencyView(raw.efficiency),
|
|
104
|
+
savingsSourceId: typeof raw.savingsSourceId === "string" && /^[a-f0-9]{64}$/.test(raw.savingsSourceId) ? raw.savingsSourceId : null,
|
|
105
|
+
lifetimeSavings: lifetimeSavingsView(raw.lifetimeSavings) };
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
export async function readMonitors(directory) {
|
|
@@ -247,9 +250,10 @@ export function formatMonitor(result, columns = process.stdout.columns ?? 120, h
|
|
|
247
250
|
let shown = 0, shownPcs = 0, shownAgents = 0;
|
|
248
251
|
const footer = [
|
|
249
252
|
"-".repeat(width),
|
|
250
|
-
|
|
253
|
+
...formatLifetimeSavings(result),
|
|
254
|
+
stats.length ? "Native this run - Raw: " + tokens(saved + sent) + " tokens Sent: " + tokens(sent) + " Saved: " + tokens(saved) +
|
|
251
255
|
" " + (saved + sent ? Math.round(100 * saved / (saved + sent)) : 0) + "%" + (stats.length < live.length ? " (partial)" : "") :
|
|
252
|
-
"Raw: n/a Sent: n/a Saved: n/a",
|
|
256
|
+
"Native this run - Raw: n/a Sent: n/a Saved: n/a",
|
|
253
257
|
mcp.length ? "MCP this run: " + usageSummary(mcpMetrics) + " | saved stdout " + tokens(mcpMetrics.reduce((sum, row) => sum + row.savedBytes, 0)) + " tok" :
|
|
254
258
|
"~ Tokens: bytes / 4, this run | model: declared or not reported",
|
|
255
259
|
mcpMetrics.length ? "Review: " + [...mcpMetrics].sort((a, b) => b.sentBytes - a.sentBytes).slice(0, 2)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-commander-agent",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.22-fullaccess.1",
|
|
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": {
|
package/runtime/JobHost.exe
CHANGED
|
Binary file
|
|
Binary file
|
package/runtime/OwnerBridge.exe
CHANGED
|
Binary file
|