@claude-flow/cli 3.60.1 → 3.61.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.
@@ -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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.60.1",
3
+ "version": "3.61.1",
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",