claude-flow 3.60.1 → 3.61.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/README.md CHANGED
@@ -16,6 +16,7 @@
16
16
  <br>
17
17
  <a href="https://github.com/ruvnet/claude-flow"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/d1e80400bdefe01d3b13e70395f88fffab76bb18/docs/assets/readme/badges/claude.svg" width="300" height="76" alt="Claude Code plugin"></a>
18
18
  <a href="https://www.npmjs.com/package/@claude-flow/codex"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/d1e80400bdefe01d3b13e70395f88fffab76bb18/docs/assets/readme/badges/codex.svg" width="300" height="76" alt="Codex plugin"></a>
19
+ <a href="https://chatgpt.com/plugins/plugin_asdk_app_6ab674ff53c48191bb37040c343e31f3"><img src="docs/assets/readme/badges/chatgpt.svg" width="300" height="76" alt="ChatGPT plugin: RuFlo Federation"></a>
19
20
  </p>
20
21
 
21
22
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.60.1",
3
+ "version": "3.61.0",
4
4
  "workspaces": [
5
5
  "v3/@claude-flow/codex",
6
6
  "v3/@claude-flow/mcp",
@@ -16,6 +16,7 @@
16
16
  <br>
17
17
  <a href="https://github.com/ruvnet/claude-flow"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/d1e80400bdefe01d3b13e70395f88fffab76bb18/docs/assets/readme/badges/claude.svg" width="300" height="76" alt="Claude Code plugin"></a>
18
18
  <a href="https://www.npmjs.com/package/@claude-flow/codex"><img src="https://raw.githubusercontent.com/ruvnet/ruflo/d1e80400bdefe01d3b13e70395f88fffab76bb18/docs/assets/readme/badges/codex.svg" width="300" height="76" alt="Codex plugin"></a>
19
+ <a href="https://chatgpt.com/plugins/plugin_asdk_app_6ab674ff53c48191bb37040c343e31f3"><img src="docs/assets/readme/badges/chatgpt.svg" width="300" height="76" alt="ChatGPT plugin: RuFlo Federation"></a>
19
20
  </p>
20
21
 
21
22
  </div>
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 7,
4
- "generatedAt": "2026-10-23T18:53:29.930Z",
5
- "gitSha": "af6c74b3",
4
+ "generatedAt": "2026-10-24T03:29:19.446Z",
5
+ "gitSha": "52884d98",
6
6
  "catalog": {
7
7
  "agents": 177,
8
- "tools": 438,
8
+ "tools": 439,
9
9
  "skills": 34
10
10
  },
11
11
  "benchmark": null
@@ -15,6 +15,7 @@ import { gateWithAIDefence } from '../rure/aidefence-gate.js';
15
15
  import { parseBinary, extractStrings, MAX_BINARY_BYTES } from '../rure/binary-parse.js';
16
16
  import { parseDotNet } from '../rure/dotnet-parse.js';
17
17
  import { analyzeEvm, parseHex, MAX_EVM_BYTES } from '../rure/evm-parse.js';
18
+ import { analyzeCuda } from '../rure/cuda-parse.js';
18
19
  function fail(error) {
19
20
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error }) }], isError: true };
20
21
  }
@@ -355,5 +356,57 @@ export const rureTools = [
355
356
  }
356
357
  },
357
358
  },
359
+ {
360
+ name: 'rure_analyze_cuda',
361
+ description: 'Statically inventory the GPU code in a CUDA program (ADR-0006): from a host ELF/PE with a .nv_fatbin section, a standalone fatbin, cubin or PTX file, the fatbin containers and entries (PTX or cubin, target architecture sm_NN, compression: zstd and LZ4 payloads are read under size caps), and per kernel its mangled name, code size, register count, constant/shared/local memory and an instruction estimate; PTX version, target and kernel names. Parses only: no SASS decoding, no CUDA toolkit or driver, nothing is run, and nothing is written. Names from the file are untrusted text and are screened by AIDefence (redactedCount / screened report this). Use when you need the GPU side of a CUDA program (which architectures it targets, whether it ships PTX, which kernels exist and what each costs): rure_analyze_binary alone only shows the .nv_fatbin section names, and grep or strings find kernel names only by chance. Run rure_analyze_binary first when it is not known whether a file holds CUDA code.',
362
+ category: 'rure',
363
+ tags: ['reverse-engineering', 'cuda', 'gpu', 'fatbin', 'ptx', 'static-analysis'],
364
+ inputSchema: {
365
+ type: 'object',
366
+ properties: {
367
+ path: { type: 'string', description: 'Absolute path to a CUDA host binary (ELF/PE), a .fatbin, a .cubin or a .ptx file' },
368
+ includePtx: { type: 'boolean', description: 'Also return the PTX text, cut at 64 KiB (default false)' },
369
+ },
370
+ required: ['path'],
371
+ },
372
+ handler: async (input) => {
373
+ const v = validatePath(input.path, 'path');
374
+ if (!v.valid)
375
+ return fail(v.error);
376
+ try {
377
+ const r = readCapped(v.sanitized, MAX_BINARY_BYTES);
378
+ if (!r.ok)
379
+ return fail(r.error);
380
+ const info = analyzeCuda(r.bytes, { includePtx: input.includePtx === true });
381
+ const sc = new Screener();
382
+ const names = await sc.screen(info.kernels.map((k) => k.name));
383
+ const idents = await sc.screen(info.kernels.map((k) => k.identifier ?? ''));
384
+ const kernels = info.kernels.map((k, i) => ({ ...k, name: names[i], identifier: k.identifier === null ? null : idents[i] }));
385
+ const ptx = [];
386
+ for (const m of info.ptx) {
387
+ const entries = await sc.screen(m.entries.map((e) => e.name));
388
+ const functions = await sc.screen(m.functions);
389
+ const text = m.text === undefined ? undefined : await sc.one(m.text);
390
+ ptx.push({ ...m, entries: m.entries.map((e, i) => ({ ...e, name: entries[i] })), functions, ...(text !== undefined && { text }) });
391
+ }
392
+ const hostSignals = await sc.screen(info.hostSignals);
393
+ const hostSection = info.hostSection === null ? null : { ...info.hostSection, name: await sc.one(info.hostSection.name) };
394
+ const entryKernels = kernels.filter((k) => k.isEntry).length;
395
+ return ok({
396
+ ...info,
397
+ hostSection,
398
+ hostSignals,
399
+ kernels,
400
+ ptx,
401
+ summary: { containers: info.containers, entries: info.entries.length, cubins: info.entries.filter((e) => e.kind === 'cubin').length, ptxEntries: info.entries.filter((e) => e.kind === 'ptx').length, kernels: entryKernels, deviceFunctions: kernels.length - entryKernels },
402
+ redactedCount: sc.redacted,
403
+ screened: sc.screened,
404
+ });
405
+ }
406
+ catch (err) {
407
+ return fail(`cuda analysis failed: ${err instanceof Error ? err.message : String(err)}`);
408
+ }
409
+ },
410
+ },
358
411
  ];
359
412
  //# sourceMappingURL=rure-tools.js.map
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ruRE Phase 5 — a cubin (ADR-0006): an ELF64 for the GPU (`e_machine` 190). Read here for its kernels, from the symbol table, which
3
+ * `parseBinary` does not read. Layout checked against `cuobjdump -symbols` and `-res-usage` on real nvcc output. Bounds-checked
4
+ * throughout; never throws on hostile input (a failure is a warning and what was read stays).
5
+ */
6
+ export declare const EM_CUDA = 190;
7
+ export declare const MAX_KERNELS = 500;
8
+ export interface CubinKernel {
9
+ /** The mangled name as in the symbol table. */
10
+ name: string;
11
+ /** The plain identifier of a simple `_Z<len><name>...` name, else null: full demangling is not attempted. */
12
+ identifier: string | null;
13
+ /** A kernel (launchable, `STO_CUDA_ENTRY`) or a device function (callable from the GPU only). */
14
+ isEntry: boolean;
15
+ codeBytes: number | null;
16
+ /** codeBytes / 16, for SM 7.0 and later (a SASS instruction is 16 bytes there); null otherwise. An estimate, not a decode. */
17
+ instructionEstimate: number | null;
18
+ /** The register count from the global .nv.info section (EIATTR_REGCOUNT: a symbol index and a count); null when no record names this symbol. */
19
+ registers: number | null;
20
+ constantBytes: number | null;
21
+ sharedBytes: number | null;
22
+ localBytes: number | null;
23
+ }
24
+ export interface CubinInfo {
25
+ /** SM number from `e_flags` (80 for sm_80), or null. */
26
+ archFlag: number | null;
27
+ sectionCount: number;
28
+ kernels: CubinKernel[];
29
+ warnings: string[];
30
+ truncated: boolean;
31
+ }
32
+ export declare const isCubin: (b: Buffer) => boolean;
33
+ /** `_Z5saxpyifPKfPf` -> `saxpy`; anything with a namespace, template or other prefix -> null. */
34
+ export declare function simpleIdentifier(name: string): string | null;
35
+ export declare function parseCubin(b: Buffer): CubinInfo;
36
+ //# sourceMappingURL=cuda-cubin.d.ts.map
@@ -0,0 +1,148 @@
1
+ /**
2
+ * ruRE Phase 5 — a cubin (ADR-0006): an ELF64 for the GPU (`e_machine` 190). Read here for its kernels, from the symbol table, which
3
+ * `parseBinary` does not read. Layout checked against `cuobjdump -symbols` and `-res-usage` on real nvcc output. Bounds-checked
4
+ * throughout; never throws on hostile input (a failure is a warning and what was read stays).
5
+ */
6
+ export const EM_CUDA = 190;
7
+ const MAX_SECTIONS = 4096;
8
+ const MAX_SYMBOLS = 2000;
9
+ export const MAX_KERNELS = 500;
10
+ const NAME_MAX = 200;
11
+ const STO_CUDA_ENTRY = 0x10;
12
+ const EIATTR_REGCOUNT = 0x2f;
13
+ const MAX_INFO_RECORDS = 4096;
14
+ export const isCubin = (b) => b.length >= 0x40 && b[0] === 0x7f && b.toString('latin1', 1, 4) === 'ELF' && b[4] === 2 && b[5] === 1 && b.readUInt16LE(0x12) === EM_CUDA;
15
+ const cstr = (b, at, end) => {
16
+ if (at < 0 || at >= end)
17
+ return '';
18
+ let stop = at;
19
+ while (stop < end && stop - at < NAME_MAX && b[stop] !== 0)
20
+ stop += 1;
21
+ return b.toString('latin1', at, stop);
22
+ };
23
+ /** The register count per symbol index from the global .nv.info section: records of (format u8, attribute u8, size u16) and a payload. */
24
+ function regCounts(b, offset, size) {
25
+ const out = new Map();
26
+ const end = Math.min(b.length, offset + size);
27
+ let p = offset;
28
+ for (let n = 0; p + 4 <= end && n < MAX_INFO_RECORDS; n++) {
29
+ const format = b[p];
30
+ const attribute = b[p + 1];
31
+ const length = b.readUInt16LE(p + 2);
32
+ // Format 4 carries `length` payload bytes; the other formats seen (1, 3) carry nothing after the 4-byte header.
33
+ if (format === 4) {
34
+ if (p + 4 + length > end)
35
+ break;
36
+ if (attribute === EIATTR_REGCOUNT && length === 8)
37
+ out.set(b.readUInt32LE(p + 4), b.readUInt32LE(p + 8));
38
+ p += 4 + length;
39
+ }
40
+ else {
41
+ p += 4;
42
+ }
43
+ }
44
+ return out;
45
+ }
46
+ /** `_Z5saxpyifPKfPf` -> `saxpy`; anything with a namespace, template or other prefix -> null. */
47
+ export function simpleIdentifier(name) {
48
+ const m = /^_Z(\d{1,3})([A-Za-z_]\w*)$/.exec(name);
49
+ if (m === null)
50
+ return null;
51
+ const length = Number(m[1]);
52
+ const rest = m[2];
53
+ return length > 0 && length <= rest.length ? rest.slice(0, length) : null;
54
+ }
55
+ export function parseCubin(b) {
56
+ const info = { archFlag: null, sectionCount: 0, kernels: [], warnings: [], truncated: false };
57
+ if (!isCubin(b)) {
58
+ info.warnings.push('not a CUDA ELF (64-bit little-endian, e_machine 190)');
59
+ return info;
60
+ }
61
+ try {
62
+ const flags = b.readUInt32LE(0x30);
63
+ info.archFlag = (flags >>> 8) & 0xff;
64
+ const shoff = Number(b.readBigUInt64LE(0x28));
65
+ const entsize = b.readUInt16LE(0x3a);
66
+ let count = b.readUInt16LE(0x3c);
67
+ const strIndex = b.readUInt16LE(0x3e);
68
+ if (entsize !== 64 || shoff <= 0 || shoff + count * 64 > b.length) {
69
+ info.warnings.push('the section header table is missing or runs past the file');
70
+ return info;
71
+ }
72
+ if (count > MAX_SECTIONS) {
73
+ count = MAX_SECTIONS;
74
+ info.truncated = true;
75
+ info.warnings.push(`more than ${MAX_SECTIONS} sections: the rest are not read`);
76
+ }
77
+ info.sectionCount = count;
78
+ const secs = [];
79
+ const header = (i) => shoff + i * 64;
80
+ const strOffset = strIndex < count ? Number(b.readBigUInt64LE(header(strIndex) + 24)) : 0;
81
+ const strSize = strIndex < count ? Number(b.readBigUInt64LE(header(strIndex) + 32)) : 0;
82
+ for (let i = 0; i < count; i++) {
83
+ const h = header(i);
84
+ secs.push({
85
+ name: cstr(b, strOffset + b.readUInt32LE(h), Math.min(b.length, strOffset + strSize)),
86
+ type: b.readUInt32LE(h + 4),
87
+ offset: Number(b.readBigUInt64LE(h + 24)),
88
+ size: Number(b.readBigUInt64LE(h + 32)),
89
+ link: b.readUInt32LE(h + 40),
90
+ info: b.readUInt32LE(h + 44),
91
+ });
92
+ }
93
+ const sizeOf = new Map();
94
+ for (const s of secs)
95
+ if (!sizeOf.has(s.name))
96
+ sizeOf.set(s.name, s.size);
97
+ const symtab = secs.find(s => s.type === 2);
98
+ const globalInfo = secs.find(s => s.name === '.nv.info');
99
+ const regs = globalInfo !== undefined && globalInfo.offset + globalInfo.size <= b.length ? regCounts(b, globalInfo.offset, globalInfo.size) : new Map();
100
+ const strtab = symtab === undefined ? undefined : secs[symtab.link];
101
+ if (symtab === undefined || strtab === undefined) {
102
+ info.warnings.push('no symbol table: no kernels are listed');
103
+ return info;
104
+ }
105
+ if (symtab.offset + symtab.size > b.length || strtab.offset + strtab.size > b.length) {
106
+ info.warnings.push('the symbol table runs past the file');
107
+ return info;
108
+ }
109
+ const strEnd = strtab.offset + strtab.size;
110
+ const total = Math.min(Math.floor(symtab.size / 24), MAX_SYMBOLS);
111
+ if (Math.floor(symtab.size / 24) > MAX_SYMBOLS) {
112
+ info.truncated = true;
113
+ info.warnings.push(`more than ${MAX_SYMBOLS} symbols: the rest are not read`);
114
+ }
115
+ for (let i = 0; i < total; i++) {
116
+ const at = symtab.offset + i * 24;
117
+ const type = b[at + 4] & 0xf;
118
+ const bind = b[at + 4] >> 4;
119
+ if (type !== 2 || bind !== 1)
120
+ continue;
121
+ if (info.kernels.length >= MAX_KERNELS) {
122
+ info.truncated = true;
123
+ info.warnings.push(`more than ${MAX_KERNELS} functions: the rest are not read`);
124
+ break;
125
+ }
126
+ const name = cstr(b, strtab.offset + b.readUInt32LE(at), strEnd);
127
+ const sec = secs[b.readUInt16LE(at + 6)];
128
+ const code = sec !== undefined && sec.name.startsWith('.text.') ? sec : undefined;
129
+ const archNumber = info.archFlag ?? 0;
130
+ info.kernels.push({
131
+ name,
132
+ identifier: simpleIdentifier(name),
133
+ isEntry: (b[at + 5] & STO_CUDA_ENTRY) !== 0,
134
+ codeBytes: code?.size ?? null,
135
+ instructionEstimate: code !== undefined && archNumber >= 70 ? Math.floor(code.size / 16) : null,
136
+ registers: regs.get(i) ?? null,
137
+ constantBytes: sizeOf.get(`.nv.constant0.${name}`) ?? null,
138
+ sharedBytes: sizeOf.get(`.nv.shared.${name}`) ?? null,
139
+ localBytes: sizeOf.get(`.nv.local.${name}`) ?? null,
140
+ });
141
+ }
142
+ }
143
+ catch (err) {
144
+ info.warnings.push(`cubin parse stopped: ${err instanceof RangeError ? 'out-of-bounds read' : String(err).slice(0, 100)}`);
145
+ }
146
+ return info;
147
+ }
148
+ //# sourceMappingURL=cuda-cubin.js.map
@@ -0,0 +1,48 @@
1
+ export declare const FATBIN_MAGIC = 3126193488;
2
+ export declare const MAX_CONTAINERS = 64;
3
+ export declare const MAX_ENTRIES = 256;
4
+ /** The most one entry may decompress to, and the most all entries of one file may. */
5
+ export declare const MAX_ENTRY_BYTES: number;
6
+ export declare const MAX_TOTAL_BYTES: number;
7
+ export type Compression = 'none' | 'zstd' | 'lz4' | 'unknown';
8
+ export type Zstd = (input: Buffer, maxOutput: number) => Buffer;
9
+ export declare const defaultZstd: Zstd | null;
10
+ export interface RawEntry {
11
+ container: number;
12
+ index: number;
13
+ kind: number;
14
+ headerSize: number;
15
+ paddedSize: number;
16
+ compressedSize: number;
17
+ version: number;
18
+ archNumber: number;
19
+ objNameOffset: number;
20
+ objNameLen: number;
21
+ flags: bigint;
22
+ decompressedSize: number;
23
+ /** Where the payload starts in the buffer, and how many bytes of it are really there. */
24
+ payloadOffset: number;
25
+ payloadLength: number;
26
+ }
27
+ /** Walks the containers in b[start, end). A malformed header ends the walk with a warning; what was read stays. */
28
+ export declare function walkFatbin(b: Buffer, start: number, end: number, warnings: string[]): {
29
+ entries: RawEntry[];
30
+ containers: number;
31
+ truncated: boolean;
32
+ };
33
+ /**
34
+ * An LZ4 block (no frame), as CUDA 12 writes. Original: reads only inside `src`, writes only inside `maxOut`, refuses a match offset
35
+ * that points before the start of the output. Throws RangeError on any malformed input.
36
+ */
37
+ export declare function lz4Block(src: Buffer, maxOut: number): Buffer;
38
+ export interface Payload {
39
+ /** The bytes of the cubin or the PTX text, or null when they could not be read (the reason is in `note`). */
40
+ data: Buffer | null;
41
+ compressed: Compression;
42
+ note?: string;
43
+ }
44
+ /** The entry's payload as bytes, decompressed when it is and when it may be. `budget.left` is spent by what each attempt claims, win or lose. */
45
+ export declare function readPayload(b: Buffer, entry: RawEntry, zstd: Zstd | null, budget: {
46
+ left: number;
47
+ }): Payload;
48
+ //# sourceMappingURL=cuda-fatbin.d.ts.map
@@ -0,0 +1,186 @@
1
+ /**
2
+ * ruRE Phase 5 — the CUDA fatbin container (ADR-0006). A `.nv_fatbin` section is several containers back to back; each is a 16-byte
3
+ * header (magic 0xBA55ED50, version, header size, payload size) and entries (PTX text or a cubin, possibly compressed). Layout read from
4
+ * real nvcc output, not from a specification. Parses, never executes; every offset is bounds-checked; decompression is capped twice
5
+ * (the claimed size is refused first, and the decompressor is handed the cap as its own limit).
6
+ */
7
+ import * as zlib from 'node:zlib';
8
+ export const FATBIN_MAGIC = 0xba55ed50;
9
+ export const MAX_CONTAINERS = 64;
10
+ export const MAX_ENTRIES = 256;
11
+ /** The most one entry may decompress to, and the most all entries of one file may. */
12
+ export const MAX_ENTRY_BYTES = 16 * 1024 * 1024;
13
+ export const MAX_TOTAL_BYTES = 64 * 1024 * 1024;
14
+ const FLAG_LZ4 = 0x2000n;
15
+ const FLAG_ZSTD = 0x8000n;
16
+ const ZSTD_MAGIC = Buffer.from([0x28, 0xb5, 0x2f, 0xfd]);
17
+ /**
18
+ * zstd where this Node has it (22.15 and later); null where it does not, which is said, not hidden. A namespace import and a type check, not a
19
+ * named import: on Node 20 `zstdDecompressSync` is not an export, and a named import of it would stop the whole module from loading.
20
+ */
21
+ const zstdFn = zlib.zstdDecompressSync;
22
+ export const defaultZstd = typeof zstdFn === 'function' ? (input, max) => zstdFn(input, { maxOutputLength: max }) : null;
23
+ const num = (v) => (v > BigInt(Number.MAX_SAFE_INTEGER) ? Number.MAX_SAFE_INTEGER : Number(v));
24
+ /** Walks the containers in b[start, end). A malformed header ends the walk with a warning; what was read stays. */
25
+ export function walkFatbin(b, start, end, warnings) {
26
+ const entries = [];
27
+ let containers = 0;
28
+ let pos = start;
29
+ let truncated = false;
30
+ while (pos + 16 <= end && b.readUInt32LE(pos) === FATBIN_MAGIC) {
31
+ if (containers >= MAX_CONTAINERS) {
32
+ truncated = true;
33
+ warnings.push(`more than ${MAX_CONTAINERS} fatbin containers: the rest are not read`);
34
+ break;
35
+ }
36
+ const headerSize = b.readUInt16LE(pos + 6);
37
+ const claimed = num(b.readBigUInt64LE(pos + 8));
38
+ if (headerSize < 16 || headerSize > 4096 || pos + headerSize > end) {
39
+ warnings.push(`container ${containers}: header size ${headerSize} is out of range, the walk stops`);
40
+ break;
41
+ }
42
+ const limit = Math.min(end, pos + headerSize + claimed);
43
+ if (pos + headerSize + claimed > end)
44
+ warnings.push(`container ${containers}: its size (${claimed}) runs past the data, read up to the end`);
45
+ containers += 1;
46
+ let p = pos + headerSize;
47
+ while (p + 16 <= limit) {
48
+ if (entries.length >= MAX_ENTRIES) {
49
+ truncated = true;
50
+ warnings.push(`more than ${MAX_ENTRIES} fatbin entries: the rest are not read`);
51
+ return { entries, containers, truncated };
52
+ }
53
+ const size = b.readUInt32LE(p + 4);
54
+ if (size < 16 || size > 4096 || p + size > limit) {
55
+ warnings.push(`container ${containers - 1}: an entry header length of ${size} is out of range, this container's walk stops`);
56
+ break;
57
+ }
58
+ const padded = num(b.readBigUInt64LE(p + 8));
59
+ const full = size >= 64;
60
+ const payloadOffset = p + size;
61
+ const payloadLength = Math.max(0, Math.min(padded, limit - payloadOffset));
62
+ if (!full)
63
+ warnings.push(`container ${containers - 1}: an entry header of ${size} bytes is shorter than the 64 that carry its fields`);
64
+ if (padded > limit - payloadOffset)
65
+ warnings.push(`container ${containers - 1}: an entry claims ${padded} payload bytes, only ${limit - payloadOffset} are there`);
66
+ entries.push({
67
+ container: containers - 1,
68
+ index: entries.length,
69
+ kind: b.readUInt16LE(p),
70
+ headerSize: size,
71
+ paddedSize: padded,
72
+ compressedSize: full ? b.readUInt32LE(p + 16) : 0,
73
+ version: full ? b.readUInt32LE(p + 24) : 0,
74
+ archNumber: full ? b.readUInt32LE(p + 28) : 0,
75
+ objNameOffset: full ? b.readUInt32LE(p + 32) : 0,
76
+ objNameLen: full ? b.readUInt32LE(p + 36) : 0,
77
+ flags: full ? b.readBigUInt64LE(p + 40) : 0n,
78
+ decompressedSize: full ? num(b.readBigUInt64LE(p + 56)) : 0,
79
+ payloadOffset,
80
+ payloadLength,
81
+ });
82
+ p += size + padded;
83
+ }
84
+ pos = limit;
85
+ }
86
+ const rest = b.subarray(pos, end);
87
+ if (rest.length > 0 && rest.some(byte => byte !== 0))
88
+ warnings.push(`${rest.length} bytes after the last container are not a fatbin`);
89
+ return { entries, containers, truncated };
90
+ }
91
+ /**
92
+ * An LZ4 block (no frame), as CUDA 12 writes. Original: reads only inside `src`, writes only inside `maxOut`, refuses a match offset
93
+ * that points before the start of the output. Throws RangeError on any malformed input.
94
+ */
95
+ export function lz4Block(src, maxOut) {
96
+ const out = Buffer.allocUnsafe(maxOut);
97
+ let ip = 0;
98
+ let op = 0;
99
+ while (ip < src.length) {
100
+ const token = src[ip++];
101
+ let literals = token >> 4;
102
+ if (literals === 15) {
103
+ let more;
104
+ do {
105
+ if (ip >= src.length)
106
+ throw new RangeError('literal length runs past the input');
107
+ more = src[ip++];
108
+ literals += more;
109
+ if (literals > maxOut)
110
+ throw new RangeError('literal length past the cap');
111
+ } while (more === 255);
112
+ }
113
+ if (ip + literals > src.length)
114
+ throw new RangeError('literals run past the input');
115
+ if (op + literals > maxOut)
116
+ throw new RangeError('output past the cap');
117
+ src.copy(out, op, ip, ip + literals);
118
+ ip += literals;
119
+ op += literals;
120
+ if (ip >= src.length)
121
+ break;
122
+ if (ip + 2 > src.length)
123
+ throw new RangeError('match offset runs past the input');
124
+ const offset = src.readUInt16LE(ip);
125
+ ip += 2;
126
+ if (offset === 0 || offset > op)
127
+ throw new RangeError('match offset points before the start of the output');
128
+ let length = token & 15;
129
+ if (length === 15) {
130
+ let more;
131
+ do {
132
+ if (ip >= src.length)
133
+ throw new RangeError('match length runs past the input');
134
+ more = src[ip++];
135
+ length += more;
136
+ if (length > maxOut)
137
+ throw new RangeError('match length past the cap');
138
+ } while (more === 255);
139
+ }
140
+ length += 4;
141
+ if (op + length > maxOut)
142
+ throw new RangeError('output past the cap');
143
+ // Byte by byte: a match may overlap what it is writing (that is how a run is encoded).
144
+ for (let i = 0; i < length; i++)
145
+ out[op + i] = out[op - offset + i];
146
+ op += length;
147
+ }
148
+ return out.subarray(0, op);
149
+ }
150
+ /** The entry's payload as bytes, decompressed when it is and when it may be. `budget.left` is spent by what each attempt claims, win or lose. */
151
+ export function readPayload(b, entry, zstd, budget) {
152
+ const raw = b.subarray(entry.payloadOffset, entry.payloadOffset + entry.payloadLength);
153
+ const isZstd = (entry.flags & FLAG_ZSTD) !== 0n;
154
+ const isLz4 = (entry.flags & FLAG_LZ4) !== 0n;
155
+ if (!isZstd && !isLz4)
156
+ return { data: raw, compressed: 'none' };
157
+ if (isZstd && isLz4)
158
+ return { data: null, compressed: 'unknown', note: 'both the zstd and the lz4 flag are set: not read' };
159
+ const kind = isZstd ? 'zstd' : 'lz4';
160
+ const claimed = entry.decompressedSize;
161
+ if (claimed <= 0)
162
+ return { data: null, compressed: kind, note: 'the header gives no decompressed size: not read' };
163
+ if (claimed > MAX_ENTRY_BYTES)
164
+ return { data: null, compressed: kind, note: `claims ${claimed} bytes decompressed: over the ${MAX_ENTRY_BYTES}-byte cap, not read` };
165
+ if (claimed > budget.left)
166
+ return { data: null, compressed: kind, note: 'the total decompression budget for this file is spent: not read' };
167
+ const packed = raw.subarray(0, entry.compressedSize > 0 ? Math.min(entry.compressedSize, raw.length) : raw.length);
168
+ if (isZstd) {
169
+ if (!packed.subarray(0, 4).equals(ZSTD_MAGIC))
170
+ return { data: null, compressed: 'unknown', note: 'the zstd flag is set but the payload does not start with a zstd frame: not read' };
171
+ if (zstd === null)
172
+ return { data: null, compressed: 'zstd', note: 'this Node cannot read zstd (it needs 22.15 or later)' };
173
+ }
174
+ // Charged when the attempt starts, not when it succeeds: a stream that fails late must not be retryable by 256 entries for 4 GiB of work.
175
+ budget.left -= claimed;
176
+ try {
177
+ const out = isZstd ? zstd(packed, claimed) : lz4Block(packed, claimed);
178
+ if (out.length !== claimed)
179
+ return { data: null, compressed: kind, note: `decompressed to ${out.length} bytes, the header says ${claimed}: not used` };
180
+ return { data: out, compressed: kind };
181
+ }
182
+ catch (err) {
183
+ return { data: null, compressed: kind, note: `could not be decompressed: ${err instanceof Error ? err.message.slice(0, 120) : 'error'}` };
184
+ }
185
+ }
186
+ //# sourceMappingURL=cuda-fatbin.js.map
@@ -0,0 +1,68 @@
1
+ /**
2
+ * ruRE Phase 5 — CUDA fatbin and cubin static analysis (ADR-0006). One entry point: `analyzeCuda(bytes)` decides what it was handed from
3
+ * the bytes (a host ELF or PE with a `.nv_fatbin` section, a standalone fatbin, a standalone cubin, or PTX text), walks the containers,
4
+ * decompresses what may be decompressed under the caps, and returns the inventory: architectures, PTX, kernels and what each costs.
5
+ * Parses, never executes; no CUDA toolkit or driver is touched.
6
+ */
7
+ import { MAX_BINARY_BYTES } from './binary-parse.js';
8
+ import { type Compression, type Zstd } from './cuda-fatbin.js';
9
+ import { type CubinKernel } from './cuda-cubin.js';
10
+ import { type PtxInfo } from './cuda-ptx.js';
11
+ export { MAX_BINARY_BYTES };
12
+ export type CudaSource = 'host' | 'fatbin' | 'cubin' | 'ptx';
13
+ export interface CudaEntry {
14
+ index: number;
15
+ container: number;
16
+ kind: 'ptx' | 'cubin' | 'unknown';
17
+ rawKind: number;
18
+ arch: string | null;
19
+ /** Reported raw: its meaning is not documented. */
20
+ version: string;
21
+ flags: string;
22
+ compressed: Compression;
23
+ storedBytes: number;
24
+ /** Bytes after decompression, or null when the payload could not be read. */
25
+ size: number | null;
26
+ read: boolean;
27
+ note?: string;
28
+ objName?: {
29
+ offset: number;
30
+ length: number;
31
+ };
32
+ }
33
+ export interface CudaKernel extends CubinKernel {
34
+ /** The index of the entry (cubin) it was read from, and that entry's architecture. */
35
+ entry: number | null;
36
+ arch: string | null;
37
+ }
38
+ export interface CudaPtxModule extends PtxInfo {
39
+ entry: number | null;
40
+ }
41
+ export interface CudaInfo {
42
+ source: CudaSource;
43
+ /** Where the fatbin was in a host file. */
44
+ hostSection: {
45
+ name: string;
46
+ offset: number;
47
+ size: number;
48
+ } | null;
49
+ /** Names in a host file that say it uses CUDA: sections, libraries, driver and runtime imports. */
50
+ hostSignals: string[];
51
+ containers: number;
52
+ entries: CudaEntry[];
53
+ /** Distinct `sm_NN` of the cubins and of the PTX, sorted. */
54
+ cubinArchs: string[];
55
+ ptxArchs: string[];
56
+ kernels: CudaKernel[];
57
+ ptx: CudaPtxModule[];
58
+ warnings: string[];
59
+ truncated: boolean;
60
+ }
61
+ export interface CudaOptions {
62
+ includePtx?: boolean;
63
+ /** Overridable so a Node without zstd can be tested; null means "this runtime cannot read zstd". */
64
+ zstd?: Zstd | null;
65
+ }
66
+ /** What the bytes are and what is in them. Never throws on hostile input: a failure is a warning. */
67
+ export declare function analyzeCuda(b: Buffer, opts?: CudaOptions): CudaInfo;
68
+ //# sourceMappingURL=cuda-parse.d.ts.map
@@ -0,0 +1,127 @@
1
+ /**
2
+ * ruRE Phase 5 — CUDA fatbin and cubin static analysis (ADR-0006). One entry point: `analyzeCuda(bytes)` decides what it was handed from
3
+ * the bytes (a host ELF or PE with a `.nv_fatbin` section, a standalone fatbin, a standalone cubin, or PTX text), walks the containers,
4
+ * decompresses what may be decompressed under the caps, and returns the inventory: architectures, PTX, kernels and what each costs.
5
+ * Parses, never executes; no CUDA toolkit or driver is touched.
6
+ */
7
+ import { parseBinary, MAX_BINARY_BYTES } from './binary-parse.js';
8
+ import { defaultZstd, FATBIN_MAGIC, MAX_TOTAL_BYTES, readPayload, walkFatbin } from './cuda-fatbin.js';
9
+ import { isCubin, parseCubin } from './cuda-cubin.js';
10
+ import { looksLikePtx, scanPtx } from './cuda-ptx.js';
11
+ export { MAX_BINARY_BYTES };
12
+ const FATBIN_SECTIONS = new Set(['.nv_fatbin', '.nv_fatb', '__nv_fatbin']);
13
+ const SIGNAL_SECTION = /^(?:\.nv|__nv|\.nvFatBin)/;
14
+ const SIGNAL_LIBRARY = /cuda|cudart|cublas|cudnn|cufft|curand|cusparse|nvrtc|nvcuda|nvml/i;
15
+ const SIGNAL_IMPORT = /^(?:cu[A-Z]\w*|__cuda\w+|__cudaRegister\w+)$/;
16
+ const SIGNAL_MAX = 24;
17
+ const archOf = (n) => (n > 0 && n < 1000 ? `sm_${n}` : null);
18
+ const kindName = (k) => (k === 1 ? 'ptx' : k === 2 ? 'cubin' : 'unknown');
19
+ const empty = (source) => ({ source, hostSection: null, hostSignals: [], containers: 0, entries: [], cubinArchs: [], ptxArchs: [], kernels: [], ptx: [], warnings: [], truncated: false });
20
+ /** Reads the entries of one or more fatbin containers found in b[start, end) and fills `info`. */
21
+ function readFatbin(b, start, end, info, opts) {
22
+ const zstd = opts.zstd === undefined ? defaultZstd : opts.zstd;
23
+ const budget = { left: MAX_TOTAL_BYTES };
24
+ const walked = walkFatbin(b, start, end, info.warnings);
25
+ info.containers = walked.containers;
26
+ info.truncated = info.truncated || walked.truncated;
27
+ if (walked.containers === 0)
28
+ info.warnings.push('no fatbin container (magic 0xBA55ED50) at the start of the section');
29
+ for (const raw of walked.entries)
30
+ info.entries.push(readEntry(b, raw, zstd, budget, info, opts));
31
+ const archs = (kind) => [...new Set(info.entries.filter(e => e.kind === kind && e.arch !== null).map(e => e.arch))].sort();
32
+ info.cubinArchs = archs('cubin');
33
+ info.ptxArchs = archs('ptx');
34
+ }
35
+ function readEntry(b, raw, zstd, budget, info, opts) {
36
+ const kind = kindName(raw.kind);
37
+ const payload = readPayload(b, raw, zstd, budget);
38
+ const arch = archOf(raw.archNumber);
39
+ const entry = {
40
+ index: raw.index,
41
+ container: raw.container,
42
+ kind,
43
+ rawKind: raw.kind,
44
+ arch,
45
+ version: `0x${raw.version.toString(16)}`,
46
+ flags: `0x${raw.flags.toString(16)}`,
47
+ compressed: payload.compressed,
48
+ storedBytes: raw.compressedSize > 0 ? raw.compressedSize : raw.payloadLength,
49
+ size: payload.data === null ? null : payload.data.length,
50
+ read: payload.data !== null,
51
+ ...(payload.note !== undefined && { note: payload.note }),
52
+ ...(raw.objNameOffset !== 0 || raw.objNameLen !== 0 ? { objName: { offset: raw.objNameOffset, length: raw.objNameLen } } : {}),
53
+ };
54
+ if (payload.data === null)
55
+ return entry;
56
+ if (kind === 'cubin') {
57
+ if (!isCubin(payload.data)) {
58
+ entry.read = false;
59
+ entry.note = 'a cubin entry whose payload is not a CUDA ELF: not read';
60
+ return entry;
61
+ }
62
+ const cubin = parseCubin(payload.data);
63
+ info.warnings.push(...cubin.warnings.map(w => `entry ${raw.index}: ${w}`));
64
+ info.truncated = info.truncated || cubin.truncated;
65
+ if (arch !== null && cubin.archFlag !== null && cubin.archFlag !== 0 && cubin.archFlag !== raw.archNumber) {
66
+ info.warnings.push(`entry ${raw.index}: the entry header says ${arch}, the cubin's e_flags say sm_${cubin.archFlag}`);
67
+ }
68
+ for (const k of cubin.kernels)
69
+ info.kernels.push({ ...k, entry: raw.index, arch });
70
+ }
71
+ else if (kind === 'ptx') {
72
+ const text = payload.data.toString('latin1').replace(/\0+$/, '');
73
+ info.ptx.push({ ...scanPtx(text, opts.includePtx === true), entry: raw.index });
74
+ }
75
+ return entry;
76
+ }
77
+ /** Names in a host binary that say it is a CUDA program (never anything but its own names; nothing is loaded). */
78
+ function hostSignalsOf(host) {
79
+ const out = [
80
+ ...host.sections.map(s => s.name).filter(n => SIGNAL_SECTION.test(n)),
81
+ ...host.libraries.filter(n => SIGNAL_LIBRARY.test(n)),
82
+ ...host.imports.filter(n => SIGNAL_IMPORT.test(n)).slice(0, 12),
83
+ ];
84
+ return [...new Set(out)].slice(0, SIGNAL_MAX);
85
+ }
86
+ /** What the bytes are and what is in them. Never throws on hostile input: a failure is a warning. */
87
+ export function analyzeCuda(b, opts = {}) {
88
+ if (b.length >= 4 && b.readUInt32LE(0) === FATBIN_MAGIC) {
89
+ const info = empty('fatbin');
90
+ readFatbin(b, 0, b.length, info, opts);
91
+ return info;
92
+ }
93
+ if (isCubin(b)) {
94
+ const info = empty('cubin');
95
+ const cubin = parseCubin(b);
96
+ const arch = cubin.archFlag === null ? null : archOf(cubin.archFlag);
97
+ info.warnings.push(...cubin.warnings);
98
+ info.truncated = cubin.truncated;
99
+ info.kernels = cubin.kernels.map(k => ({ ...k, entry: null, arch }));
100
+ info.cubinArchs = arch === null ? [] : [arch];
101
+ return info;
102
+ }
103
+ if (looksLikePtx(b)) {
104
+ const info = empty('ptx');
105
+ const ptx = scanPtx(b.toString('latin1'), opts.includePtx === true);
106
+ info.ptx = [{ ...ptx, entry: null }];
107
+ info.ptxArchs = ptx.target === null ? [] : [ptx.target];
108
+ return info;
109
+ }
110
+ const info = empty('host');
111
+ const host = parseBinary(b);
112
+ info.warnings.push(...host.warnings);
113
+ info.hostSignals = hostSignalsOf(host);
114
+ const section = host.sections.find(s => FATBIN_SECTIONS.has(s.name));
115
+ if (section === undefined) {
116
+ info.warnings.push(`no .nv_fatbin section: this does not look like a CUDA program (format ${host.format})`);
117
+ return info;
118
+ }
119
+ info.hostSection = { name: section.name, offset: section.offset, size: section.size };
120
+ if (section.offset < 0 || section.size <= 0 || section.offset + section.size > b.length) {
121
+ info.warnings.push('the .nv_fatbin section runs past the file: not read');
122
+ return info;
123
+ }
124
+ readFatbin(b, section.offset, section.offset + section.size, info, opts);
125
+ return info;
126
+ }
127
+ //# sourceMappingURL=cuda-parse.js.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * ruRE Phase 5 — PTX text (ADR-0006): NVIDIA's virtual ISA. Only a bounded scan for the header directives and the kernel and function
3
+ * names; the text is never interpreted further and never run. The scan reads at most 4 MiB, and every pattern is linear.
4
+ */
5
+ export declare const MAX_PTX_SCAN: number;
6
+ export declare const MAX_PTX_TEXT: number;
7
+ export interface PtxInfo {
8
+ version: string | null;
9
+ target: string | null;
10
+ addressSize: number | null;
11
+ /** `.entry` kernels with the number of `.param` declarations in their signature. */
12
+ entries: {
13
+ name: string;
14
+ params: number;
15
+ }[];
16
+ /** `.func` device functions. */
17
+ functions: string[];
18
+ /** The text itself, only when asked for, cut at MAX_PTX_TEXT. */
19
+ text?: string;
20
+ textTruncated?: boolean;
21
+ scanTruncated: boolean;
22
+ }
23
+ /**
24
+ * True when the bytes look like PTX source: the first directive is `.version` after comments and blank space. A hand-written scan, not a
25
+ * regex: a lazy comment pattern inside a repeated group backtracks exponentially on `/*` followed by many `*` + `/` pairs (CodeQL js/redos
26
+ * caught the first version of this). Every step here moves forward, so the cost is linear in the 8 KiB it looks at.
27
+ */
28
+ export declare function looksLikePtx(b: Buffer): boolean;
29
+ export declare function scanPtx(text: string, includeText: boolean): PtxInfo;
30
+ //# sourceMappingURL=cuda-ptx.d.ts.map
@@ -0,0 +1,67 @@
1
+ /**
2
+ * ruRE Phase 5 — PTX text (ADR-0006): NVIDIA's virtual ISA. Only a bounded scan for the header directives and the kernel and function
3
+ * names; the text is never interpreted further and never run. The scan reads at most 4 MiB, and every pattern is linear.
4
+ */
5
+ export const MAX_PTX_SCAN = 4 * 1024 * 1024;
6
+ export const MAX_PTX_TEXT = 64 * 1024;
7
+ const MAX_NAMES = 500;
8
+ const NAME_MAX = 200;
9
+ /**
10
+ * True when the bytes look like PTX source: the first directive is `.version` after comments and blank space. A hand-written scan, not a
11
+ * regex: a lazy comment pattern inside a repeated group backtracks exponentially on `/*` followed by many `*` + `/` pairs (CodeQL js/redos
12
+ * caught the first version of this). Every step here moves forward, so the cost is linear in the 8 KiB it looks at.
13
+ */
14
+ export function looksLikePtx(b) {
15
+ const head = b.toString('latin1', 0, Math.min(b.length, 8192));
16
+ let i = 0;
17
+ for (;;) {
18
+ while (i < head.length && (head.charCodeAt(i) <= 32 || head.charCodeAt(i) === 0xa0))
19
+ i += 1;
20
+ if (head.startsWith('//', i)) {
21
+ const end = head.indexOf('\n', i);
22
+ if (end < 0)
23
+ return false;
24
+ i = end + 1;
25
+ }
26
+ else if (head.startsWith('/*', i)) {
27
+ const end = head.indexOf('*/', i + 2);
28
+ if (end < 0)
29
+ return false;
30
+ i = end + 2;
31
+ }
32
+ else
33
+ break;
34
+ }
35
+ return /^\.version\s+\d/.test(head.slice(i, i + 32));
36
+ }
37
+ export function scanPtx(text, includeText) {
38
+ const scanned = text.length > MAX_PTX_SCAN ? text.slice(0, MAX_PTX_SCAN) : text;
39
+ const info = {
40
+ // Space and tab only, never `\s`, at a line start: `\s` also matches newlines, so every line start inside a run of blank lines rescanned the
41
+ // rest of the run (80000 blank lines took 4.6 s and 320000 took 71 s before this).
42
+ version: /^[ \t]*\.version[ \t]+(\d+\.\d+)/m.exec(scanned)?.[1] ?? null,
43
+ target: /^[ \t]*\.target[ \t]+([A-Za-z0-9_., ]{1,60}?)[ \t]*(?:\/\/|$)/m.exec(scanned)?.[1]?.trim() ?? null,
44
+ addressSize: Number(/^[ \t]*\.address_size[ \t]+(\d+)/m.exec(scanned)?.[1] ?? NaN) || null,
45
+ entries: [],
46
+ functions: [],
47
+ scanTruncated: text.length > MAX_PTX_SCAN,
48
+ };
49
+ const re = /\.(entry|func)\s{1,64}(?:\([^)]{0,200}\)\s{0,64})?([A-Za-z_$%][\w$.%]{0,198})\s{0,64}\(/g;
50
+ let m;
51
+ while ((m = re.exec(scanned)) !== null && info.entries.length + info.functions.length < MAX_NAMES) {
52
+ const name = m[2].slice(0, NAME_MAX);
53
+ if (m[1] === 'func') {
54
+ info.functions.push(name);
55
+ continue;
56
+ }
57
+ const close = scanned.indexOf(')', re.lastIndex);
58
+ const signature = scanned.slice(re.lastIndex, close < 0 || close - re.lastIndex > 4096 ? re.lastIndex + 4096 : close);
59
+ info.entries.push({ name, params: (signature.match(/\.param\b/g) ?? []).length });
60
+ }
61
+ if (includeText) {
62
+ info.text = scanned.length > MAX_PTX_TEXT ? scanned.slice(0, MAX_PTX_TEXT) : scanned;
63
+ info.textTruncated = scanned.length > MAX_PTX_TEXT;
64
+ }
65
+ return info;
66
+ }
67
+ //# sourceMappingURL=cuda-ptx.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.60.1",
3
+ "version": "3.61.0",
4
4
  "type": "module",
5
5
  "description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",