From aedccce7810f61763e7b81328ae08b5076e765ab Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 12 Sep 2025 07:48:46 +0200 Subject: [PATCH 1/5] feat: add Agent Version column to peers table shows which IPFS implementation each peer is running (e.g., kubo/0.35.0) depends on https://github.com/ipfs/js-kubo-rpc-client/pull/342 --- public/locales/en/peers.json | 1 + src/bundles/peer-locations.js | 10 ++++++++- src/bundles/peers.js | 2 +- src/peers/PeersTable/PeersTable.js | 33 +++++++++++++++++++++++------- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/public/locales/en/peers.json b/public/locales/en/peers.json index 4ccec8abf..154dedada 100644 --- a/public/locales/en/peers.json +++ b/public/locales/en/peers.json @@ -3,6 +3,7 @@ "localNetwork": "Local Network", "nearby": "nearby", "protocols": "Open streams", + "agentVersion": "Agent Version", "addConnection": "Add connection", "insertPeerAddress": "Insert the peer address you want to connect to.", "addPermanentPeer": "Add to the permanent peering configuration", diff --git a/src/bundles/peer-locations.js b/src/bundles/peer-locations.js index 141f0c24f..e34d95c63 100644 --- a/src/bundles/peer-locations.js +++ b/src/bundles/peer-locations.js @@ -90,6 +90,13 @@ function createPeersLocations (opts) { )).sort() : []).join(', ') + // Truncate agent version as a defensive measure against excessively long strings + const rawAgentVersion = peer.identify?.AgentVersion || '' + const maxAgentVersionLength = 30 + const agentVersion = rawAgentVersion.length > maxAgentVersionLength + ? rawAgentVersion.substring(0, maxAgentVersionLength) + '…' + : rawAgentVersion + return { peerId, location, @@ -101,7 +108,8 @@ function createPeersLocations (opts) { direction, latency, isPrivate, - isNearby + isNearby, + agentVersion } })) ) diff --git a/src/bundles/peers.js b/src/bundles/peers.js index e317648c3..837c6f858 100644 --- a/src/bundles/peers.js +++ b/src/bundles/peers.js @@ -6,7 +6,7 @@ const swarmPeersTTL = ms.seconds(10) const bundle = createAsyncResourceBundle({ name: 'peers', actionBaseType: 'PEERS', - getPromise: ({ getIpfs }) => getIpfs().swarm.peers({ verbose: true, timeout: swarmPeersTTL }), + getPromise: ({ getIpfs }) => getIpfs().swarm.peers({ verbose: true, identify: true, timeout: swarmPeersTTL }), staleAfter: swarmPeersTTL, persist: false, checkIfOnline: false diff --git a/src/peers/PeersTable/PeersTable.js b/src/peers/PeersTable/PeersTable.js index 93de11880..e6b956c3f 100644 --- a/src/peers/PeersTable/PeersTable.js +++ b/src/peers/PeersTable/PeersTable.js @@ -75,6 +75,22 @@ const protocolsCellRenderer = (t) => ({ rowData }) => { ) } +const agentVersionCellRenderer = (t) => ({ rowData }) => { + const ref = React.createRef() + const { agentVersion } = rowData + if (!agentVersion) return (-) + return ( + copyFeedback(ref, t)}> + + { agentVersion } + + + ) +} + const connectionCellRenderer = (t) => ({ rowData }) => { const ref = React.createRef() const { address, direction, peerId } = rowData @@ -140,7 +156,7 @@ export const PeersTable = ({ className, t, peerLocationsForSwarm, selectedPeers const filteredPeerList = useMemo(() => { const filterLower = filter.toLowerCase() if (filterLower === '') return awaitedPeerLocationsForSwarm - return awaitedPeerLocationsForSwarm.filter(({ location, latency, peerId, connection, protocols }) => { + return awaitedPeerLocationsForSwarm.filter(({ location, latency, peerId, connection, protocols, agentVersion }) => { if (location != null && location.toLowerCase().includes(filterLower)) { return true } @@ -150,13 +166,15 @@ export const PeersTable = ({ className, t, peerLocationsForSwarm, selectedPeers if (peerId != null && peerId.toString().includes(filter)) { return true } - console.log('connection: ', connection) if (connection != null && connection.toLowerCase().includes(filterLower)) { return true } if (protocols != null && protocols.toLowerCase().includes(filterLower)) { return true } + if (agentVersion != null && agentVersion.toLowerCase().includes(filterLower)) { + return true + } return false }) @@ -186,11 +204,12 @@ export const PeersTable = ({ className, t, peerLocationsForSwarm, selectedPeers sort={sort} sortBy={sortBy} sortDirection={sortDirection}> - - - - - + + + + + + )} From 850c49bbb863b661e0834f1aab3c4e0b13d19894 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 12 Sep 2025 08:52:51 +0200 Subject: [PATCH 2/5] fix: increase agent version truncation to 64 chars aligns with kubo's limit from ipfs/kubo#9465 --- src/bundles/peer-locations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bundles/peer-locations.js b/src/bundles/peer-locations.js index e34d95c63..e2b7fb129 100644 --- a/src/bundles/peer-locations.js +++ b/src/bundles/peer-locations.js @@ -92,7 +92,7 @@ function createPeersLocations (opts) { // Truncate agent version as a defensive measure against excessively long strings const rawAgentVersion = peer.identify?.AgentVersion || '' - const maxAgentVersionLength = 30 + const maxAgentVersionLength = 64 // same as https://github.com/ipfs/kubo/pull/9465 const agentVersion = rawAgentVersion.length > maxAgentVersionLength ? rawAgentVersion.substring(0, maxAgentVersionLength) + '…' : rawAgentVersion From 5cd32937f05660ade05b69b534cf94b26344e054 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 16 Sep 2025 04:19:25 +0200 Subject: [PATCH 3/5] chore: update kubo-rpc-client to v5.3.0 - includes identify field support from ipfs/js-kubo-rpc-client#342 - add @babel/plugin-proposal-private-property-in-object to fix build warning - update browserslist database --- .aegir.js | 4 ++ package-lock.json | 61 +++++++++++++++--------------- package.json | 3 +- src/bundles/peer-locations.test.js | 5 +++ 4 files changed, 42 insertions(+), 31 deletions(-) diff --git a/.aegir.js b/.aegir.js index e7782801e..d77240543 100644 --- a/.aegir.js +++ b/.aegir.js @@ -34,6 +34,10 @@ export default { 'tachyons', 'uint8arrays', + // babel-preset-react-app uses this without declaring it as a dependency + // see: https://github.com/facebook/create-react-app/issues/13325 + '@babel/plugin-proposal-private-property-in-object', + // type-only deps 'ipfs', diff --git a/package-lock.json b/package-lock.json index ee587b8d9..1af71925f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "it-first": "^1.0.6", "it-last": "^1.0.5", "it-map": "^1.0.5", - "kubo-rpc-client": "^5.2.0", + "kubo-rpc-client": "^5.3.0", "milliseconds": "^1.0.3", "money-clip": "^3.0.5", "multiformats": "^13.4.0", @@ -77,6 +77,7 @@ }, "devDependencies": { "@babel/core": "^7.18.9", + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/preset-react": "^7.18.6", "@ipld/dag-pb": "^4.0.8", "@jest/globals": "^29.7.0", @@ -861,10 +862,18 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, "engines": { "node": ">=6.9.0" }, @@ -2239,6 +2248,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/preset-flow": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.25.9.tgz", @@ -12253,26 +12274,6 @@ "@babel/core": "^7.4.0-0" } }, - "node_modules/@storybook/core-common/node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", - "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@storybook/core-common/node_modules/@storybook/node-logger": { "version": "6.5.16", "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.5.16.tgz", @@ -28025,9 +28026,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001684", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", - "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", + "version": "1.0.30001741", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz", + "integrity": "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==", "funding": [ { "type": "opencollective", @@ -47956,9 +47957,9 @@ } }, "node_modules/kubo-rpc-client": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/kubo-rpc-client/-/kubo-rpc-client-5.2.0.tgz", - "integrity": "sha512-J3ppL1xf7f27NDI9jUPGkr1QiExXLyxUTUwHUMMB1a4AZR4s6113SVXPHRYwe1pFIO3hRb5G+0SuHaxYSfhzBA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/kubo-rpc-client/-/kubo-rpc-client-5.3.0.tgz", + "integrity": "sha512-uky2WMSpXxA+GMNLOBLmpR5W7kSMvwCh72FKjz2PNhrB5frHiXboJbYhFewAJ9N0FNnC2E7VUerGqBDAfLSrQg==", "license": "Apache-2.0 OR MIT", "dependencies": { "@ipld/dag-cbor": "^9.0.0", diff --git a/package.json b/package.json index 3ff077c24..29e240709 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "it-first": "^1.0.6", "it-last": "^1.0.5", "it-map": "^1.0.5", - "kubo-rpc-client": "^5.2.0", + "kubo-rpc-client": "^5.3.0", "milliseconds": "^1.0.3", "money-clip": "^3.0.5", "multiformats": "^13.4.0", @@ -107,6 +107,7 @@ }, "devDependencies": { "@babel/core": "^7.18.9", + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/preset-react": "^7.18.6", "@ipld/dag-pb": "^4.0.8", "@jest/globals": "^29.7.0", diff --git a/src/bundles/peer-locations.test.js b/src/bundles/peer-locations.test.js index cf5f322cf..92438831c 100644 --- a/src/bundles/peer-locations.test.js +++ b/src/bundles/peer-locations.test.js @@ -189,6 +189,7 @@ describe('selectPeerLocationsForSwarm', () => { expect(result).toEqual([ { address: '1.test', + agentVersion: '', connection: '1/test • endOfTest', coordinates: [1.11, 1.01], direction: undefined, @@ -202,6 +203,7 @@ describe('selectPeerLocationsForSwarm', () => { }, { address: '2.test', + agentVersion: '', connection: '2/test • endOfTest', coordinates: [2.22, 2.02], direction: undefined, @@ -252,8 +254,10 @@ describe('selectPeerLocationsForSwarm', () => { expect(result).toEqual([ { address: '1.test', + agentVersion: '', connection: '1/test • endOfTest', coordinates: [1.11, 1.01], + direction: undefined, flagCode: 'ROM', isNearby: false, isPrivate: true, @@ -292,6 +296,7 @@ describe('selectPeerLocationsForSwarm', () => { expect(result).toEqual([ { address: '1.test', + agentVersion: '', connection: '1/test • endOfTest', direction: undefined, coordinates: null, From ade4d860d89e3d8bbe1e4a706c21766c0e089d48 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 16 Sep 2025 04:47:27 +0200 Subject: [PATCH 4/5] refactor: remove agent version truncation - removes client-side truncation as it will be handled upstream in kubo via ipfs/kubo#9465 - simplifies agentVersion to use undefined instead of empty string when not present --- src/bundles/peer-locations.js | 8 ++------ src/bundles/peer-locations.test.js | 8 ++++---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/bundles/peer-locations.js b/src/bundles/peer-locations.js index e2b7fb129..29a1189f1 100644 --- a/src/bundles/peer-locations.js +++ b/src/bundles/peer-locations.js @@ -90,12 +90,8 @@ function createPeersLocations (opts) { )).sort() : []).join(', ') - // Truncate agent version as a defensive measure against excessively long strings - const rawAgentVersion = peer.identify?.AgentVersion || '' - const maxAgentVersionLength = 64 // same as https://github.com/ipfs/kubo/pull/9465 - const agentVersion = rawAgentVersion.length > maxAgentVersionLength - ? rawAgentVersion.substring(0, maxAgentVersionLength) + '…' - : rawAgentVersion + // Get agent version (truncation will be handled upstream in kubo via https://github.com/ipfs/kubo/pull/9465) + const agentVersion = peer.identify?.AgentVersion return { peerId, diff --git a/src/bundles/peer-locations.test.js b/src/bundles/peer-locations.test.js index 92438831c..97e83ac1c 100644 --- a/src/bundles/peer-locations.test.js +++ b/src/bundles/peer-locations.test.js @@ -189,7 +189,7 @@ describe('selectPeerLocationsForSwarm', () => { expect(result).toEqual([ { address: '1.test', - agentVersion: '', + agentVersion: undefined, connection: '1/test • endOfTest', coordinates: [1.11, 1.01], direction: undefined, @@ -203,7 +203,7 @@ describe('selectPeerLocationsForSwarm', () => { }, { address: '2.test', - agentVersion: '', + agentVersion: undefined, connection: '2/test • endOfTest', coordinates: [2.22, 2.02], direction: undefined, @@ -254,7 +254,7 @@ describe('selectPeerLocationsForSwarm', () => { expect(result).toEqual([ { address: '1.test', - agentVersion: '', + agentVersion: undefined, connection: '1/test • endOfTest', coordinates: [1.11, 1.01], direction: undefined, @@ -296,7 +296,7 @@ describe('selectPeerLocationsForSwarm', () => { expect(result).toEqual([ { address: '1.test', - agentVersion: '', + agentVersion: undefined, connection: '1/test • endOfTest', direction: undefined, coordinates: null, From 195c8572df37607312669f8f0251aecc5915acd9 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 16 Sep 2025 05:17:51 +0200 Subject: [PATCH 5/5] refactor: compact connection column display - use slash notation (ip4/tcp) instead of bullets - shorten quic-v1 to quic - use monospace font like other technical columns --- src/bundles/peer-locations.js | 5 ++++- src/bundles/peer-locations.test.js | 8 ++++---- src/peers/PeersTable/PeersTable.js | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/bundles/peer-locations.js b/src/bundles/peer-locations.js index 29a1189f1..adf01d343 100644 --- a/src/bundles/peer-locations.js +++ b/src/bundles/peer-locations.js @@ -161,7 +161,10 @@ const toLocationString = loc => { } const parseConnection = (multiaddr) => { - return multiaddr.protoNames().join(' • ') + const protocols = multiaddr.protoNames() + .map(p => p.startsWith('quic-v') ? 'quic' : p) // shorten quic-v1, quic-v2, etc to just 'quic' + .join('/') + return protocols } const parseLatency = (latency) => { diff --git a/src/bundles/peer-locations.test.js b/src/bundles/peer-locations.test.js index 97e83ac1c..aaa416dbd 100644 --- a/src/bundles/peer-locations.test.js +++ b/src/bundles/peer-locations.test.js @@ -190,7 +190,7 @@ describe('selectPeerLocationsForSwarm', () => { { address: '1.test', agentVersion: undefined, - connection: '1/test • endOfTest', + connection: '1/test/endOfTest', coordinates: [1.11, 1.01], direction: undefined, flagCode: 'ROM', @@ -204,7 +204,7 @@ describe('selectPeerLocationsForSwarm', () => { { address: '2.test', agentVersion: undefined, - connection: '2/test • endOfTest', + connection: '2/test/endOfTest', coordinates: [2.22, 2.02], direction: undefined, flagCode: 'ROM', @@ -255,7 +255,7 @@ describe('selectPeerLocationsForSwarm', () => { { address: '1.test', agentVersion: undefined, - connection: '1/test • endOfTest', + connection: '1/test/endOfTest', coordinates: [1.11, 1.01], direction: undefined, flagCode: 'ROM', @@ -297,7 +297,7 @@ describe('selectPeerLocationsForSwarm', () => { { address: '1.test', agentVersion: undefined, - connection: '1/test • endOfTest', + connection: '1/test/endOfTest', direction: undefined, coordinates: null, flagCode: null, diff --git a/src/peers/PeersTable/PeersTable.js b/src/peers/PeersTable/PeersTable.js index e6b956c3f..e63bdd9a8 100644 --- a/src/peers/PeersTable/PeersTable.js +++ b/src/peers/PeersTable/PeersTable.js @@ -207,7 +207,7 @@ export const PeersTable = ({ className, t, peerLocationsForSwarm, selectedPeers - +