@claude-flow/cli 3.63.0 → 3.63.2

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": {
3
- "version": "3.63.0",
3
+ "version": "3.63.2",
4
4
  "files": {
5
5
  "auto-memory-hook.mjs": "631fac9d518835e2852a406888316bbbddddb49e7ed8e406d2e971f46833df9a",
6
6
  "hook-handler.cjs": "24502a3bcb78770971147940515c992e35b6736ae14ee2dc54e07d4cdef6a1d8",
@@ -9,6 +9,6 @@
9
9
  "router.cjs": "16648b3c0d73b6681ac2d8d13def6a68286c57f5a11004de5b19a0fa088c833d"
10
10
  }
11
11
  },
12
- "signature": "xwg3vfgr6Zy0tIcVGzVhiqL9TmbFIrx6gJFzkQ3AhX0Ei38/t4EVIR7GSB2dlXJ0CrrSvWVohDHGNbIpo92CBQ==",
12
+ "signature": "6+Obx8HZs73gmcPZ6JJH6sBZf3OGb1DC5p5p0K8QhMAAr1QwhKu2uNPv36NmjpsbOTJ0MpidudSRt4fP/ToVBA==",
13
13
  "algorithm": "ed25519"
14
14
  }
package/README.md CHANGED
@@ -244,7 +244,7 @@ This adds slash commands and agent definitions. `ruflo-core` (installed above) d
244
244
  | [**ruflo-browser**](plugins/ruflo-browser/README.md) | Automate browser testing with Playwright |
245
245
  | [**ruflo-jujutsu**](plugins/ruflo-jujutsu/README.md) | Analyze git diffs, score risk, suggest reviewers |
246
246
  | [**ruflo-docs**](plugins/ruflo-docs/README.md) | Generate and maintain documentation automatically |
247
- | [**agentic-qe-fleet**](https://github.com/proffesor-for-testing/agentic-qe/tree/v3.15.12/plugins/agentic-qe-fleet) | External, maintained by [Agentic QE](https://github.com/proffesor-for-testing/agentic-qe): 11 QE agents, test generation, coverage, chaos and TDD skills, the `agentic-qe` MCP server, and a guard for AQE learning data (`/plugin install agentic-qe-fleet@ruflo`). Install it from one marketplace only, `agentic-qe` or `ruflo`: enabled from both, its mod and commands load twice |
247
+ | [**agentic-qe-fleet**](https://github.com/proffesor-for-testing/agentic-qe/tree/v3.15.13/plugins/agentic-qe-fleet) | External, maintained by [Agentic QE](https://github.com/proffesor-for-testing/agentic-qe): 11 QE agents, test generation, coverage, chaos and TDD skills, the `agentic-qe` MCP server, and a guard for AQE learning data (`/plugin install agentic-qe-fleet@ruflo`). Install it from one marketplace only, `agentic-qe` or `ruflo`: enabled from both, its mod and commands load twice |
248
248
 
249
249
  #### Security & Compliance
250
250
 
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 7,
4
- "generatedAt": "2026-10-25T03:04:54.913Z",
5
- "gitSha": "e2dae2f7",
4
+ "generatedAt": "2026-10-26T14:15:30.738Z",
5
+ "gitSha": "a52ae16e",
6
6
  "catalog": {
7
7
  "agents": 177,
8
8
  "tools": 444,
@@ -441,14 +441,19 @@ export const agentbbsTools = [
441
441
  const logPath = roomLogPath(basePath, roomId);
442
442
  const all = readEnvelopes(logPath);
443
443
  let slice = all;
444
+ let resuming = false;
444
445
  if (sinceEnvelopeId) {
445
446
  const idx = all.findIndex(e => e.envelopeId === sinceEnvelopeId);
446
- slice = idx >= 0 ? all.slice(idx + 1) : all;
447
+ resuming = idx >= 0;
448
+ slice = resuming ? all.slice(idx + 1) : all;
447
449
  }
448
450
  // Dedup by envelopeId before applying the limit — a relay replay or a
449
451
  // double-append must not surface the same envelope twice to a watcher.
450
452
  const deduped = dedupEnvelopes(slice);
451
- const envelopes = deduped.slice(-limit);
453
+ // A watcher resuming after sinceEnvelopeId gets the oldest envelopes
454
+ // first, so passing the last returned id pages through the rest when
455
+ // hasMore is set. Without a cursor, it gets the most recent window.
456
+ const envelopes = resuming ? deduped.slice(0, limit) : deduped.slice(-limit);
452
457
  return {
453
458
  success: true,
454
459
  roomId,
@@ -61,11 +61,33 @@ export function validateUrl(rawUrl) {
61
61
  function isPrivateOrLoopback(host) {
62
62
  if (host === 'localhost' || host === 'localhost.localdomain')
63
63
  return true;
64
- // IPv6 loopback
65
- if (host === '::1' || host === '[::1]')
66
- return true;
64
+ // Node's URL parser keeps brackets in `.hostname` for every IPv6 literal
65
+ // (e.g. "[::1]", "[fc00::1]", "[::ffff:7f00:1]"). Strip them once so every
66
+ // check below operates on the bare address — without this, the fc00::/7
67
+ // and fe80::/10 checks further down never match anything (they test
68
+ // `startsWith('fc'/'fe80:')` against a string starting with "["), and an
69
+ // IPv4-mapped-IPv6 literal (`::ffff:a.b.c.d`, which Node normalizes to a
70
+ // canonical `::ffff:xxxx:yyyy` hex form) sails through both the IPv4 and
71
+ // IPv6 branches untested — a live SSRF bypass (e.g. `::ffff:127.0.0.1` or
72
+ // `::ffff:169.254.169.254` reaching loopback / cloud-metadata targets).
73
+ const bare = host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host;
74
+ if (bare === '::1')
75
+ return true; // IPv6 loopback
76
+ // IPv4-mapped IPv6 — decode back to dotted-decimal and recurse into the
77
+ // IPv4 check below so it inherits every existing private/loopback/
78
+ // link-local/CGNAT rule instead of needing its own copy.
79
+ const mappedHex = bare.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
80
+ if (mappedHex) {
81
+ const hi = parseInt(mappedHex[1], 16);
82
+ const lo = parseInt(mappedHex[2], 16);
83
+ const decoded = `${(hi >> 8) & 0xff}.${hi & 0xff}.${(lo >> 8) & 0xff}.${lo & 0xff}`;
84
+ return isPrivateOrLoopback(decoded);
85
+ }
86
+ const mappedDecimal = bare.match(/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/);
87
+ if (mappedDecimal)
88
+ return isPrivateOrLoopback(mappedDecimal[1]);
67
89
  // IPv4 numeric checks
68
- const m = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
90
+ const m = bare.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
69
91
  if (m) {
70
92
  const a = Number(m[1]);
71
93
  const b = Number(m[2]);
@@ -85,12 +107,18 @@ function isPrivateOrLoopback(host) {
85
107
  return true; // CGNAT
86
108
  return false;
87
109
  }
88
- // IPv6 bracketed addresses and other special forms — be conservative and
89
- // accept only public-looking literals. Reject obvious private/local forms.
90
- if (host.startsWith('fc') || host.startsWith('fd'))
110
+ // IPv6 special forms — be conservative and accept only public-looking
111
+ // literals. Reject obvious private/local forms.
112
+ if (bare.startsWith('fc') || bare.startsWith('fd'))
91
113
  return true; // fc00::/7 ULA
92
- if (host.startsWith('fe80:'))
114
+ if (bare.startsWith('fe80:'))
93
115
  return true; // link-local
116
+ // KNOWN LIMITATION (pre-existing, not introduced by the bracket-stripping /
117
+ // IPv4-mapped-IPv6 fix above; flagged by an independent adversarial review):
118
+ // `::` (IPv6 unspecified/"any", the analog of 0.0.0.0, which the IPv4 branch
119
+ // above does block), IPv6 multicast (ff00::/8), and NAT64 (64:ff9b::/96) are
120
+ // not covered. Left as a follow-up hardening item rather than scope-creeping
121
+ // this patch.
94
122
  return false;
95
123
  }
96
124
  export function validateHeaders(headers) {
@@ -54,6 +54,9 @@ async function maRequest(method, path, body, timeoutMs = 30_000) {
54
54
  const key = apiKey();
55
55
  if (!key)
56
56
  return { ok: false, status: 0, error: NEEDS_KEY.error };
57
+ // encodeURIComponent leaves '.' and '..' alone, so an id of '..' would walk up the API path (/v1/sessions/.. is /v1/).
58
+ if (path.split('?')[0].split('/').some(segment => segment === '.' || segment === '..'))
59
+ return { ok: false, status: 0, error: 'Invalid managed resource id' };
57
60
  try {
58
61
  const res = await fetch(`${apiBase()}/v1${path}`, {
59
62
  method, headers: headers(key), redirect: 'error',
@@ -91,6 +91,11 @@ function atomicWriteJson(file, value) {
91
91
  fs.closeSync(fd);
92
92
  }
93
93
  fs.renameSync(tmp, file);
94
+ // Windows cannot flush a directory handle (FlushFileBuffers fails with EPERM on NTFS), and there the journaled rename needs no such step.
95
+ // Skipping it there is the same rule policy-runtime's syncDirectory follows (#3398). On POSIX a failure here means the rename may not be
96
+ // durable, so it still reaches the caller (#4492).
97
+ if (process.platform === 'win32')
98
+ return;
94
99
  const dirFd = fs.openSync(path.dirname(file), fs.constants.O_RDONLY);
95
100
  try {
96
101
  fs.fsyncSync(dirFd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.63.0",
3
+ "version": "3.63.2",
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",