@harusame64/desktop-touch-mcp 1.10.2 → 1.10.3

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.ja.md CHANGED
@@ -59,6 +59,21 @@ npm ランチャーは npm package version に厳密に対応する runtime だ
59
59
 
60
60
  キャッシュの保存先は `DESKTOP_TOUCH_MCP_HOME` で変更できます。
61
61
 
62
+ > **共有ネットワークや CI 環境の場合:** 初回起動時に GitHub Releases API を参照して
63
+ > runtime zip を探します。匿名アクセスの上限は IP あたり 60 回/時で、共有グローバル IP
64
+ > (CI ランナー、オフィスの NAT など)ではダウンロード開始前に枯渇することがあります。
65
+ > 環境変数に `GITHUB_TOKEN`(または `GH_TOKEN`)を設定すると API 呼び出しが認証され、
66
+ > 上限が 5,000 回/時に上がります。通常の家庭回線ではトークンは不要です。
67
+
68
+ > **ソースチェックアウトから launcher を実行する場合:** ソースビルドの
69
+ > `bin/launcher.js` は確定済みの整合性ハッシュではなくプレースホルダ
70
+ > (`sha256: "PENDING"`)を持ちます。検証できない runtime をダウンロードして実行する
71
+ > 代わりに launcher は fail-closed で停止し、誤って publish された/未確定の launcher が
72
+ > 検証されていないコードを黙って起動するのを防ぎます。publish 済みの npm リリースは
73
+ > 常に本物の SHA256 を同梱するため、利用者がこの状態に遭遇することはありません。
74
+ > 意図的にソースから launcher を実行する場合は `DESKTOP_TOUCH_MCP_ALLOW_UNVERIFIED=1`
75
+ > を設定すると整合性検証をスキップできます(開発用途のみ)。
76
+
62
77
  ### Claude CLI への登録
63
78
 
64
79
  `~/.claude.json` の `mcpServers` に追加:
package/README.md CHANGED
@@ -60,6 +60,23 @@ The npm launcher resolves runtime strictly by npm package version. For package `
60
60
 
61
61
  Set `DESKTOP_TOUCH_MCP_HOME` to override the cache root directory.
62
62
 
63
+ > **On a shared or CI network?** The first run reads the GitHub Releases API to
64
+ > locate the runtime zip. The anonymous limit is 60 requests/hour per IP, which a
65
+ > shared public address (CI runners, office NAT) can exhaust before your download
66
+ > even starts. Set `GITHUB_TOKEN` (or `GH_TOKEN`) in the environment and the
67
+ > launcher authenticates the request, raising the limit to 5,000 requests/hour.
68
+ > No token is needed on an ordinary home connection.
69
+
70
+ > **Running the launcher from a source checkout?** A source build's
71
+ > `bin/launcher.js` carries a placeholder integrity hash (`sha256: "PENDING"`)
72
+ > instead of a finalized one. Rather than download and run an unverified runtime,
73
+ > the launcher fails closed — this guard stops an accidentally published or
74
+ > unfinalized launcher from silently starting unverified code. Published npm
75
+ > releases always ship a real SHA256, so end users never see this. If you are
76
+ > intentionally running the launcher from source, set
77
+ > `DESKTOP_TOUCH_MCP_ALLOW_UNVERIFIED=1` to skip integrity verification
78
+ > (development only).
79
+
63
80
  ### Register with Claude CLI
64
81
 
65
82
  Add to `~/.claude.json` under `mcpServers`:
package/bin/launcher.js CHANGED
@@ -18,15 +18,15 @@ import path from "node:path";
18
18
  import { Readable } from "node:stream";
19
19
  import { pipeline } from "node:stream/promises";
20
20
 
21
- const PACKAGE_VERSION = "1.10.2";
21
+ const PACKAGE_VERSION = "1.10.3";
22
22
  const RELEASE_TAG = `v${PACKAGE_VERSION}`;
23
23
  const REPO_API_URL = `https://api.github.com/repos/Harusame64/desktop-touch-mcp/releases/tags/${RELEASE_TAG}`;
24
24
  const ASSET_NAME = "desktop-touch-mcp-windows.zip";
25
25
  const RELEASE_METADATA_FILE = ".desktop-touch-release.json";
26
26
  const RELEASE_MANIFEST = {
27
- tagName: "v1.10.2",
27
+ tagName: "v1.10.3",
28
28
  assetName: ASSET_NAME,
29
- sha256: "178e05165da8fe087509cf00c0ef2754ce2a1d9169aa7efa53a14624c620726a",
29
+ sha256: "940b518d92f7678d02289560bddd68f34e8813e76ab5d051feb0da31d6865cf1",
30
30
  };
31
31
  const CACHE_ROOT = process.env.DESKTOP_TOUCH_MCP_HOME
32
32
  ? path.resolve(process.env.DESKTOP_TOUCH_MCP_HOME)
@@ -38,11 +38,49 @@ function log(message) {
38
38
  console.error(`[desktop-touch-mcp] ${message}`);
39
39
  }
40
40
 
41
+ function warn(message) {
42
+ console.error(`[desktop-touch-mcp] WARNING: ${message}`);
43
+ }
44
+
41
45
  function fail(message) {
42
46
  console.error(`[desktop-touch-mcp] ${message}`);
43
47
  process.exit(1);
44
48
  }
45
49
 
50
+ /**
51
+ * Opt-in escape hatch for running the launcher from a source tree whose
52
+ * RELEASE_MANIFEST.sha256 is still the "PENDING" placeholder (i.e. the
53
+ * release workflow has not finalized the manifest). Published npm packages
54
+ * always ship a real SHA256, so end users never need this.
55
+ */
56
+ function allowUnverifiedRelease() {
57
+ return process.env.DESKTOP_TOUCH_MCP_ALLOW_UNVERIFIED === "1";
58
+ }
59
+
60
+ /**
61
+ * Reads the GitHub token from the environment.
62
+ * Supports both GITHUB_TOKEN (GitHub Actions standard) and GH_TOKEN (gh CLI).
63
+ */
64
+ function getGitHubToken() {
65
+ return process.env.GITHUB_TOKEN || process.env.GH_TOKEN || null;
66
+ }
67
+
68
+ /**
69
+ * Returns headers for GitHub API and release download requests.
70
+ * Includes Authorization when a token is available to avoid rate limits.
71
+ */
72
+ function getGitHubHeaders(extra = {}) {
73
+ const headers = {
74
+ "User-Agent": "desktop-touch-mcp-launcher",
75
+ ...extra,
76
+ };
77
+ const token = getGitHubToken();
78
+ if (token) {
79
+ headers["Authorization"] = `Bearer ${token}`;
80
+ }
81
+ return headers;
82
+ }
83
+
46
84
  export function isDisconnectError(error) {
47
85
  return error?.code === "EPIPE" || error?.code === "ERR_STREAM_DESTROYED";
48
86
  }
@@ -130,13 +168,29 @@ function expectedReleaseSpec() {
130
168
  if (!RELEASE_MANIFEST.sha256 || RELEASE_MANIFEST.assetName !== ASSET_NAME) {
131
169
  throw new Error(`Missing release manifest for ${RELEASE_TAG}`);
132
170
  }
133
- if (!/^[a-f0-9]{64}$/i.test(RELEASE_MANIFEST.sha256)) {
171
+ // "PENDING" is the pre-release placeholder set in source. The release
172
+ // workflow (scripts/update-sha.mjs) replaces it with the real zip SHA256
173
+ // before npm publish, so a PENDING manifest at runtime means this launcher
174
+ // was not finalized — fail closed by default so an accidentally published
175
+ // launcher can never silently run an unverified runtime zip. Developers
176
+ // running straight from source can opt in to skipping verification with
177
+ // DESKTOP_TOUCH_MCP_ALLOW_UNVERIFIED=1.
178
+ const isPending = RELEASE_MANIFEST.sha256 === "PENDING";
179
+ if (isPending && !allowUnverifiedRelease()) {
180
+ throw new Error(
181
+ `Release SHA256 manifest for ${RELEASE_TAG} is PENDING — this launcher was not finalized by the release workflow. ` +
182
+ `Published npm packages always ship a real SHA256. If you are intentionally running the launcher from source, ` +
183
+ `set DESKTOP_TOUCH_MCP_ALLOW_UNVERIFIED=1 to skip integrity verification (development only).`
184
+ );
185
+ }
186
+ if (!isPending && !/^[a-f0-9]{64}$/i.test(RELEASE_MANIFEST.sha256)) {
134
187
  throw new Error(`Invalid release SHA256 manifest for ${RELEASE_TAG}`);
135
188
  }
136
189
  return {
137
190
  tagName: RELEASE_MANIFEST.tagName,
138
191
  assetName: RELEASE_MANIFEST.assetName,
139
- sha256: String(RELEASE_MANIFEST.sha256).toLowerCase(),
192
+ sha256: isPending ? null : String(RELEASE_MANIFEST.sha256).toLowerCase(),
193
+ sha256Pending: isPending,
140
194
  };
141
195
  }
142
196
 
@@ -153,11 +207,12 @@ async function isInstalled(releaseDir, expected) {
153
207
  if (!existsSync(path.join(releaseDir, "dist", "index.js"))) return false;
154
208
  const metadata = await readReleaseMetadata(releaseDir);
155
209
  if (!metadata) return false;
156
- return (
157
- metadata.tagName === expected.tagName &&
158
- metadata.assetName === expected.assetName &&
159
- String(metadata.sha256 || "").toLowerCase() === expected.sha256
160
- );
210
+ if (metadata.tagName !== expected.tagName || metadata.assetName !== expected.assetName) return false;
211
+ // Skip SHA256 check when the manifest is still PENDING.
212
+ if (expected.sha256 !== null) {
213
+ if (String(metadata.sha256 || "").toLowerCase() !== expected.sha256) return false;
214
+ }
215
+ return true;
161
216
  }
162
217
 
163
218
  async function readCurrentRelease(expected) {
@@ -167,7 +222,7 @@ async function readCurrentRelease(expected) {
167
222
  if (typeof parsed?.tagName !== "string") return null;
168
223
  if (parsed.tagName !== expected.tagName) return null;
169
224
  if (parsed.assetName !== expected.assetName) return null;
170
- if (String(parsed.sha256 || "").toLowerCase() !== expected.sha256) return null;
225
+ if (expected.sha256 !== null && String(parsed.sha256 || "").toLowerCase() !== expected.sha256) return null;
171
226
  const releaseDir = releaseDirForTag(parsed.tagName);
172
227
  if (!(await isInstalled(releaseDir, expected))) return null;
173
228
  return { tagName: parsed.tagName, releaseDir };
@@ -195,10 +250,9 @@ async function writeCurrentRelease(expected) {
195
250
 
196
251
  async function fetchReleaseByTag(expected) {
197
252
  const response = await fetch(REPO_API_URL, {
198
- headers: {
253
+ headers: getGitHubHeaders({
199
254
  "Accept": "application/vnd.github+json",
200
- "User-Agent": "desktop-touch-mcp-launcher",
201
- },
255
+ }),
202
256
  });
203
257
 
204
258
  if (!response.ok) {
@@ -249,9 +303,7 @@ async function verifySha256(filePath, expectedSha256) {
249
303
 
250
304
  async function downloadFile(url, destination) {
251
305
  const response = await fetch(url, {
252
- headers: {
253
- "User-Agent": "desktop-touch-mcp-launcher",
254
- },
306
+ headers: getGitHubHeaders(),
255
307
  });
256
308
 
257
309
  if (!response.ok) {
@@ -314,7 +366,12 @@ async function installRelease(release, expected) {
314
366
  try {
315
367
  log(`Downloading ${ASSET_NAME} from ${release.tagName}`);
316
368
  await downloadFile(release.assetUrl, zipPath);
317
- await verifySha256(zipPath, expected.sha256);
369
+ if (expected.sha256 !== null) {
370
+ await verifySha256(zipPath, expected.sha256);
371
+ } else {
372
+ warn("SHA256 manifest is PENDING and DESKTOP_TOUCH_MCP_ALLOW_UNVERIFIED=1 is set — " +
373
+ "skipping integrity verification of the downloaded zip. Development use only.");
374
+ }
318
375
  await mkdir(extractDir, { recursive: true });
319
376
  await expandZip(zipPath, extractDir);
320
377
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harusame64/desktop-touch-mcp",
3
- "version": "1.10.2",
3
+ "version": "1.10.3",
4
4
  "mcpName": "io.github.Harusame64/desktop-touch-mcp",
5
5
  "description": "Let Claude, Cursor, or any MCP client see and operate your Windows 10/11 desktop. 29 tools for screenshots, UI Automation, Chrome CDP, keyboard/mouse, terminal, with semantic discover-then-act targeting and per-action perception guards that avoid wrong-window typing and stale-coordinate clicks.",
6
6
  "keywords": [
@@ -107,7 +107,7 @@
107
107
  "@modelcontextprotocol/sdk": "^1.10.0",
108
108
  "@napi-rs/cli": "^3.7.0",
109
109
  "@nut-tree-fork/nut-js": "^4.2.6",
110
- "@types/node": "^25.9.1",
110
+ "@types/node": "^25.9.2",
111
111
  "@types/ws": "^8.18.1",
112
112
  "eslint": "^10.4.1",
113
113
  "fast-check": "^4.8.0",