puppet-mcp 4.5.2 → 4.5.5

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 CHANGED
@@ -7,12 +7,12 @@ manifest — it contains no server bytes.
7
7
  ## Run
8
8
 
9
9
  ```sh
10
- npx puppet-mcp@4.5.2
10
+ npx puppet-mcp@latest
11
11
  ```
12
12
 
13
13
  Global installs are refused by design (`npm i -g` exits non-zero): a
14
- global copy would shadow the pinned `npx` invocation and run a stale
15
- engine. Always invoke the pinned version above.
14
+ global copy would shadow the `npx` invocation and run a stale engine.
15
+ Always invoke it through `npx` as above.
16
16
 
17
17
  Or pin it in an MCP client as a stdio server with command and args:
18
18
 
@@ -30,13 +30,14 @@ stdout belongs to the MCP channel.
30
30
  ## Diagnostics (no network)
31
31
 
32
32
  ```sh
33
- npx puppet-mcp@4.5.2 --doctor
33
+ npx puppet-mcp@latest --doctor
34
34
  ```
35
35
 
36
36
  Prints the launcher version, the pinned manifest, the resolved engine
37
37
  path, the engine version parsed from the cache directory name, and
38
38
  whether they match; exits 0, performs no fetch. `--print-manifest` is
39
- an alias. `npx puppet-mcp@4.5.2 --version` prints `puppet-mcp 4.5.2`.
39
+ an alias. `npx puppet-mcp@latest --version` prints the package name and
40
+ the version it is running.
40
41
 
41
42
  ## Requirements
42
43
 
@@ -56,6 +57,11 @@ There is no fully offline fresh install: the first serve run still downloads
56
57
  the pinned binary, so treat these as pins and overrides, not as a no-network
57
58
  setup.
58
59
 
60
+ ## Full documentation
61
+
62
+ https://github.com/nathwn12/puppet/blob/main/README.md
63
+ https://www.npmjs.com/package/puppet-mcp
64
+
59
65
  ## License
60
66
 
61
67
  MIT — see LICENSE.
package/bin/puppet-mcp.js CHANGED
@@ -48,6 +48,12 @@ const MANIFEST_PATH = path.join(PKG_DIR, 'engine-manifest.json');
48
48
  const PKG_PATH = path.join(PKG_DIR, 'package.json');
49
49
  const PLATFORM_OVERRIDE_ENV = 'PUPPET_MCP_ALLOW_NON_WINDOWS';
50
50
  const RUNTIME_CACHE_OVERRIDE_ENV = 'PUPPET_ENGINE_CACHE';
51
+ // Local-bundle override, diagnostics only. A normal run leaves it unset and
52
+ // downloads runtime.archive from the Release. The pre-publish cleanroom names
53
+ // the archive it just built, because the Release that would serve it does not
54
+ // exist until AFTER the build job passes. Every size/sha256 assertion still
55
+ // runs on the local bytes: an override can never smuggle in a bad artifact.
56
+ const RUNTIME_BUNDLE_OVERRIDE_ENV = 'PUPPET_ENGINE_BUNDLE';
51
57
  const DOWNLOAD_TIMEOUT_MS = 15 * 60 * 1000;
52
58
  // Staging/extraction guards: the real runtime bundle is ~2 MB. These caps
53
59
  // reject zip bombs, never the real artifact.
@@ -177,6 +183,12 @@ function printDoctor(manifest, pkgVersion) {
177
183
  runtimeCacheDir: runtime.dir,
178
184
  runtimeCacheOverridden: runtime.overridden,
179
185
  runtimeCacheNotes: runtime.notes,
186
+ runtimeBundleOverride: {
187
+ env: RUNTIME_BUNDLE_OVERRIDE_ENV,
188
+ set: Boolean(process.env[RUNTIME_BUNDLE_OVERRIDE_ENV]),
189
+ path: process.env[RUNTIME_BUNDLE_OVERRIDE_ENV] ?? null,
190
+ note: 'diagnostics only: the pre-publish cleanroom points this at the archive it just built; a normal run leaves it unset and downloads runtime.archive, whose size and sha256 are checked either way',
191
+ },
180
192
  runtime: manifest.runtime,
181
193
  network: 'none: this diagnostic performs no fetch and exits before any download code',
182
194
  };
@@ -195,6 +207,8 @@ function usage(exitCode, stream) {
195
207
  ' --help, -h print this usage',
196
208
  '',
197
209
  'runtime cache: ' + path.join('%LOCALAPPDATA%', 'puppet', 'engine', '<serverVersion>-<bundleSha8>') + ` (override: ${RUNTIME_CACHE_OVERRIDE_ENV})`,
210
+ `local bundle: unset by default; ${RUNTIME_BUNDLE_OVERRIDE_ENV} names a zip file to verify and extract`,
211
+ ' instead of downloading (pre-publish diagnostics only; size and sha256 are still checked)',
198
212
  `platform: windows-x86_64 only (override: ${PLATFORM_OVERRIDE_ENV}=1, diagnostics only)`,
199
213
  ].join('\n') + '\n';
200
214
  stream.write(text);
@@ -385,21 +399,38 @@ async function ensureRuntime(manifest, cacheDir) {
385
399
  return cacheDir;
386
400
  }
387
401
 
388
- const url = `${rt.source}/${rt.archive.name}`;
389
- log(`provisioning runtime cache: ${cacheDir}`);
390
- log(`downloading ${url} (${rt.archive.size} bytes)`);
402
+ // Local bundle override: the pre-publish cleanroom builds the archive in
403
+ // this same job, before any GitHub Release exists, so it hands the zip over
404
+ // instead of fetching a URL that cannot exist yet. The size and sha256
405
+ // checks below stay unconditional: a local zip that disagrees with the
406
+ // manifest is refused exactly like a corrupt download would be.
407
+ const localBundle = process.env[RUNTIME_BUNDLE_OVERRIDE_ENV];
408
+ const origin = localBundle ? 'local bundle' : 'download';
391
409
  let data;
392
- try {
393
- data = await downloadToMemory(url);
394
- } catch (err) {
395
- fail(err.message);
410
+ if (localBundle) {
411
+ log(`provisioning runtime cache: ${cacheDir}`);
412
+ log(`reading ${localBundle} (${RUNTIME_BUNDLE_OVERRIDE_ENV} set; pre-publish diagnostics only)`);
413
+ try {
414
+ data = fs.readFileSync(localBundle);
415
+ } catch (err) {
416
+ fail(`cannot read local bundle ${localBundle}: ${err.message}`);
417
+ }
418
+ } else {
419
+ const url = `${rt.source}/${rt.archive.name}`;
420
+ log(`provisioning runtime cache: ${cacheDir}`);
421
+ log(`downloading ${url} (${rt.archive.size} bytes)`);
422
+ try {
423
+ data = await downloadToMemory(url);
424
+ } catch (err) {
425
+ fail(err.message);
426
+ }
396
427
  }
397
428
  if (data.length !== rt.archive.size) {
398
- fail(`download size ${data.length} != manifest ${rt.archive.size}; wrote nothing.`);
429
+ fail(`${origin} size ${data.length} != manifest ${rt.archive.size}; wrote nothing.`);
399
430
  }
400
431
  const digest = crypto.createHash('sha256').update(data).digest('hex');
401
432
  if (digest !== rt.archive.sha256.toLowerCase()) {
402
- fail('download sha256 mismatch; wrote nothing.');
433
+ fail(`${origin} sha256 mismatch; wrote nothing.`);
403
434
  }
404
435
 
405
436
  // Extract into a staging dir, verify, then rename: the target dir is
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "_comment": "Pinned server manifest for the puppet-mcp launcher. The published package holds ONLY this launcher plus this manifest (no server bytes). On first run the launcher downloads ONLY runtime.archive from the tagged GitHub Release, verifying its sha256 before and after extraction. The release workflow fills the runtime.* hashes it computes from the real build artifacts; the committed manifest carries placeholders the launcher refuses to run with.",
3
3
  "package": "puppet-mcp",
4
- "serverVersion": "4.5.2",
4
+ "serverVersion": "4.5.5",
5
5
  "runtime": {
6
- "source": "https://github.com/nathwn12/puppet/releases/download/v4.5.2",
6
+ "source": "https://github.com/nathwn12/puppet/releases/download/v4.5.5",
7
7
  "archive": {
8
- "name": "puppet-v4.5.2-windows-x86_64.zip",
9
- "sha256": "4b0ebbf25194530fd426d6dcc1fbb54270cc4a1b8148d1ffedd5fc2f94fc83ea",
10
- "size": 687786
8
+ "name": "puppet-v4.5.5-windows-x86_64.zip",
9
+ "sha256": "2b37bd6b6eec0011b7208c9d821a641ff1c7257e20e8de19c99465c2edb5b4b4",
10
+ "size": 688301
11
11
  },
12
12
  "server": {
13
13
  "name": "puppet.exe",
14
- "sha256": "c0fb32a61583084eb0e54a6c9f5be1c2d4c85eac3761256ae547b5ae850a2e09",
14
+ "sha256": "1159d0ffda950a2f262abade2b2124af31427a15d426ddc100434307724d4f14",
15
15
  "size": 1810944
16
16
  }
17
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "puppet-mcp",
3
- "version": "4.5.2",
3
+ "version": "4.5.5",
4
4
  "description": "Windows desktop-control MCP server (npx launcher): provisions the pinned server binary from the GitHub Release on first run, then serves MCP over stdio",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -15,8 +15,8 @@
15
15
  "puppet-mcp": "./bin/puppet-mcp.js"
16
16
  },
17
17
  "scripts": {
18
- "preinstall": "node -e \"if(process.env.npm_config_global==='true'){console.error('puppet-mcp: global install refused — use npx puppet-mcp@4.5.2');process.exit(1)}\"",
19
- "postinstall": "node -e \"if(process.env.npm_config_global==='true'){console.error('puppet-mcp: global install refused — use npx puppet-mcp@4.5.2');process.exit(1)}\""
18
+ "preinstall": "node -e \"if(process.env.npm_config_global==='true'){console.error('puppet-mcp: global install refused — use npx puppet-mcp@4.5.4');process.exit(1)}\"",
19
+ "postinstall": "node -e \"if(process.env.npm_config_global==='true'){console.error('puppet-mcp: global install refused — use npx puppet-mcp@4.5.4');process.exit(1)}\""
20
20
  },
21
21
  "files": [
22
22
  "bin/",