bantamkit-mcp 0.27.0 → 0.28.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/dist/cli.js CHANGED
@@ -51,12 +51,13 @@
51
51
  * reaches for is answered on the wire instead, by `build_identity`. `runtime-ts/README.md`
52
52
  * and `docs/install.md` carry the same correction in their own words.
53
53
  */
54
- import { readFileSync, readdirSync } from 'node:fs';
54
+ import { readFileSync } from 'node:fs';
55
55
  import { assetsRoot } from './assets.js';
56
56
  import { BantamError } from './errors.js';
57
57
  import { buildReport, resolveEventLogPath } from './mcpreport.js';
58
58
  import { Memory } from './memory/component.js';
59
59
  import { DEFAULT_INDEX_BUDGET } from './memory/store.js';
60
+ import { packFileCount } from './mcp/identity.js';
60
61
  import { buildServer } from './mcp/server.js';
61
62
  import { RawStdioTransport } from './mcp/transport.js';
62
63
  import { ArgvError, formatHelp, formatUsageBlock, HelpRequested, helpWidth, parseArgs as parseWithSpec, pyIntStrict, } from './pyargparse.js';
@@ -207,12 +208,10 @@ async function main(argv) {
207
208
  const options = parseArgs(argv);
208
209
  if (options.assetsRoot) {
209
210
  const root = assetsRoot();
210
- let files = 0;
211
- for (const entry of readdirSync(root, { withFileTypes: true, recursive: true })) {
212
- if (entry.isFile())
213
- files += 1;
214
- }
215
- process.stdout.write(`${root}\n${files} files\n`);
211
+ // `packFileCount`, not a second walk: the count printed here and the count
212
+ // `build_identity` reports must be the same number for the same pack, and on a
213
+ // `pip install` the unfiltered version made one process contradict itself.
214
+ process.stdout.write(`${root}\n${packFileCount(root)} files\n`);
216
215
  return 0;
217
216
  }
218
217
  if (options.mcpReport) {
@@ -10,10 +10,43 @@ export declare const SERVER_NAME = "bantamkit";
10
10
  * `as_posix()` is why the parts are joined with `/` here and not with `path.sep`: on Windows
11
11
  * the walk yields the same components and the digest must not change with the separator.
12
12
  */
13
- export declare function treeDigest(root: string): {
13
+ export declare function treeDigest(root: string, keep?: (parts: string[]) => boolean): {
14
14
  digest: string;
15
15
  files: number;
16
16
  };
17
+ /**
18
+ * Fingerprint the asset pack this build would load — every file, not just the loaded ones.
19
+ *
20
+ * `contracts/default.yaml` is the reason: `RB-P84` measured it as the one asset that differed
21
+ * between two live builds while no resource template exposed it, so it was invisible on every
22
+ * probed surface. It is also why N1 ships all 84 files rather than the 20 that are read.
23
+ *
24
+ * EXCEPT bytecode caches, and that exception is the whole point of this field. The pack ships
25
+ * `.py` fixture files, so `pip install` byte-compiles them into `__pycache__` on the way in and
26
+ * the digest of an INSTALLED Python pack stopped matching the digest of the identical npm pack
27
+ * — measured on the published 0.27.0 artifacts, `sha256:fa8372f6…` over 98 files against
28
+ * `sha256:d47dcf4b…` over 87, and deleting `__pycache__` from the wheel's pack reproduced the
29
+ * npm digest byte for byte. `cross_runtime` tells the caller to compare `assets_digest` across
30
+ * runtimes; without this rule that instruction returned a false "different" on every real
31
+ * install. The pack is what was SHIPPED, never what an interpreter later wrote beside it.
32
+ *
33
+ * The rule is spelled identically in `runtime-py`'s `_assets_fingerprint`, and it has to be:
34
+ * this side never creates a `__pycache__`, so a Node-only exclusion would still agree today
35
+ * and diverge again the moment a pack carrying one reached both runtimes.
36
+ */
37
+ export declare const notBytecodeCache: (parts: string[]) => boolean;
38
+ /**
39
+ * The pack as SHIPPED, counted — what `--assets-root` prints.
40
+ *
41
+ * Exported so the CLI shares this walk rather than keeping a second one. There are TWO
42
+ * walks over this directory and a rule spelled twice is a rule that gets fixed once: the
43
+ * first version of this fix filtered the digest alone, and a `pip install` then had one
44
+ * process contradicting itself, `build_identity` answering 87 files while `--assets-root`
45
+ * printed 98 for the pack it had just loaded. The CLI's own `readdirSync(recursive)` is
46
+ * gone with it — `walkFiles` is the walk that mirrors CPython's `rglob`, which is what the
47
+ * reference's printer uses and what the conformance case compares the count against.
48
+ */
49
+ export declare function packFileCount(root: string): number;
17
50
  /** `sha256(json.dumps(inputs, sort_keys=True, separators=(",", ":")))` — the identity. */
18
51
  export declare function buildIdFor(inputs: Record<string, string>): string;
19
52
  /**
@@ -108,8 +108,9 @@ function walkFiles(root) {
108
108
  * `as_posix()` is why the parts are joined with `/` here and not with `path.sep`: on Windows
109
109
  * the walk yields the same components and the digest must not change with the separator.
110
110
  */
111
- export function treeDigest(root) {
112
- const files = walkFiles(root);
111
+ export function treeDigest(root, keep) {
112
+ const walked = walkFiles(root);
113
+ const files = keep === undefined ? walked : walked.filter(keep);
113
114
  const running = createHash('sha256');
114
115
  for (const parts of files) {
115
116
  const payload = readFileSync(join(root, ...parts));
@@ -164,7 +165,35 @@ function codeFingerprint() {
164
165
  * `contracts/default.yaml` is the reason: `RB-P84` measured it as the one asset that differed
165
166
  * between two live builds while no resource template exposed it, so it was invisible on every
166
167
  * probed surface. It is also why N1 ships all 84 files rather than the 20 that are read.
168
+ *
169
+ * EXCEPT bytecode caches, and that exception is the whole point of this field. The pack ships
170
+ * `.py` fixture files, so `pip install` byte-compiles them into `__pycache__` on the way in and
171
+ * the digest of an INSTALLED Python pack stopped matching the digest of the identical npm pack
172
+ * — measured on the published 0.27.0 artifacts, `sha256:fa8372f6…` over 98 files against
173
+ * `sha256:d47dcf4b…` over 87, and deleting `__pycache__` from the wheel's pack reproduced the
174
+ * npm digest byte for byte. `cross_runtime` tells the caller to compare `assets_digest` across
175
+ * runtimes; without this rule that instruction returned a false "different" on every real
176
+ * install. The pack is what was SHIPPED, never what an interpreter later wrote beside it.
177
+ *
178
+ * The rule is spelled identically in `runtime-py`'s `_assets_fingerprint`, and it has to be:
179
+ * this side never creates a `__pycache__`, so a Node-only exclusion would still agree today
180
+ * and diverge again the moment a pack carrying one reached both runtimes.
181
+ */
182
+ export const notBytecodeCache = (parts) => !parts.includes('__pycache__');
183
+ /**
184
+ * The pack as SHIPPED, counted — what `--assets-root` prints.
185
+ *
186
+ * Exported so the CLI shares this walk rather than keeping a second one. There are TWO
187
+ * walks over this directory and a rule spelled twice is a rule that gets fixed once: the
188
+ * first version of this fix filtered the digest alone, and a `pip install` then had one
189
+ * process contradicting itself, `build_identity` answering 87 files while `--assets-root`
190
+ * printed 98 for the pack it had just loaded. The CLI's own `readdirSync(recursive)` is
191
+ * gone with it — `walkFiles` is the walk that mirrors CPython's `rglob`, which is what the
192
+ * reference's printer uses and what the conformance case compares the count against.
167
193
  */
194
+ export function packFileCount(root) {
195
+ return walkFiles(root).filter(notBytecodeCache).length;
196
+ }
168
197
  function assetsFingerprint() {
169
198
  let root;
170
199
  try {
@@ -177,7 +206,7 @@ function assetsFingerprint() {
177
206
  }
178
207
  let walked;
179
208
  try {
180
- walked = treeDigest(root);
209
+ walked = treeDigest(root, notBytecodeCache);
181
210
  }
182
211
  catch (e) {
183
212
  const error = e instanceof PyOSError ? e : asPyOSError(e, root);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bantamkit-mcp",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "bantamkit MCP server — pure Node, runnable with npx",
5
5
  "type": "module",
6
6
  "bin": {