Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ dist/
.vscode
templates/temp-clone-folder
build/
package-lock.json
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ offckb node --network <testnet or mainnet>

Using a proxy RPC server for Testnet/Mainnet is especially helpful for debugging transactions, since failed transactions are dumped automatically.

**Watch Network With TUI**
**Watch Network with TUI**

Once you start the CKB Node, you can use `offckb status --network devnet/testnet/mainnet` to start a CKB-TUI interface to monitor the CKB network from your node.

Expand Down
7 changes: 6 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,12 @@ program
.description('Show ckb-tui status interface')
.option('--network <network>', 'Specify the network to deploy to', 'devnet')
.action(async (option) => {
status({ network: option.network });
const validNetworks = Object.values(Network);
if (!validNetworks.includes(option.network)) {
logger.error(`Invalid network: ${option.network}. Must be one of: ${validNetworks.join(', ')}`);
process.exit(1);
}
return await status({ network: option.network });
});

program
Expand Down
32 changes: 25 additions & 7 deletions src/cmd/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { readSettings } from '../cfg/setting';
import { CKBTui } from '../tools/ckb-tui';
import { Network } from '../type/base';
import { logger } from '../util/logger';
import * as net from 'net';

export interface StatusOptions {
network?: Network;
Expand All @@ -18,24 +19,41 @@ export async function status({ network }: StatusOptions) {
const url = `http://127.0.0.1:${port}`;
const isListening = await isRPCPortListening(port);
if (!isListening) {
return logger.error(
logger.error(
`RPC port ${port} is not listening. Please make sure the ${network} node is running and Proxy RPC is enabled.`,
);
return;
}
return CKBTui.runWithArgs(['-r', url]);
CKBTui.run(['-r', url]);
}

async function isRPCPortListening(port: number): Promise<boolean> {
const net = require('net');
const client = new net.Socket();
return new Promise<boolean>((resolve) => {
let settled = false;
const TIMEOUT_MS = 5000;
const timeout = setTimeout(() => {
if (!settled) {
settled = true;
client.destroy();
resolve(false);
}
}, TIMEOUT_MS);
client.once('error', () => {
resolve(false);
if (!settled) {
settled = true;
clearTimeout(timeout);
resolve(false);
}
});
client.connect(port, '127.0.0.1');
client.once('connect', () => {
client.end();
resolve(true);
if (!settled) {
settled = true;
clearTimeout(timeout);
client.end();
resolve(true);
}
});
client.connect(port, '127.0.0.1');
});
}
60 changes: 47 additions & 13 deletions src/tools/ckb-tui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@ export class CKBTui {
}

private static downloadBinary(version: string) {
// Validate version format to prevent URL manipulation
if (!/^v\d+\.\d+\.\d+$/.test(version)) {
throw new Error(`Invalid version format: ${version}. Expected format: vX.Y.Z`);
}

const platform = process.platform;
const arch = process.arch;
let assetName: string;

if (platform === 'darwin') {
if (arch === 'arm64') {
assetName = `ckb-tui-with-node-macos-aarch64.tar.gz`;
} else if (arch === 'x64') {
assetName = `ckb-tui-with-node-macos-amd64.tar.gz`;
} else {
throw new Error(`Unsupported architecture for macOS: ${arch}`);
}
Expand Down Expand Up @@ -63,9 +70,25 @@ export class CKBTui {
execSync(`unzip "${archivePath}" -d "${binDir}"`, { stdio: 'inherit' });
}

// Assume the binary is extracted as 'ckb-tui' or 'ckb-tui.exe'
// todo: fix the bin name
const extractedBinary = platform === 'win32' ? 'ckb-tui.exe' : 'ckb-tui-macos-amd64';
// Set the correct binary name based on platform and architecture
let extractedBinary: string;
if (platform === 'win32') {
extractedBinary = 'ckb-tui.exe';
} else if (platform === 'darwin') {
if (arch === 'arm64') {
extractedBinary = 'ckb-tui-macos-aarch64';
} else {
extractedBinary = 'ckb-tui-macos-amd64';
}
} else if (platform === 'linux') {
if (arch === 'x64') {
extractedBinary = 'ckb-tui-linux-amd64';
} else {
throw new Error(`Unsupported architecture for Linux: ${arch}`);
}
} else {
throw new Error(`Unsupported platform: ${platform}`);
}
const extractedPath = path.join(binDir, extractedBinary);
if (fs.existsSync(extractedPath)) {
fs.renameSync(extractedPath, this.binaryPath!);
Expand All @@ -84,18 +107,34 @@ export class CKBTui {
}
}

// Check that the binary was successfully extracted and moved
if (!fs.existsSync(this.binaryPath!)) {
logger.error(`ckb-tui binary was not found after extraction. Expected at: ${this.binaryPath}`);
throw new Error('Failed to extract and locate ckb-tui binary.');
}

// Make executable on Unix
if (platform !== 'win32') {
execSync(`chmod +x "${this.binaryPath}"`);
}

// Clean up archive
fs.unlinkSync(archivePath);

logger.info('ckb-tui installed successfully.');
} catch (error) {
logger.error('Failed to download/install ckb-tui:', (error as Error).message);
logger.error(
'Failed to download/install ckb-tui:',
(error as Error).message,
'\nPlease check your network connectivity, verify that the specified version exists in the releases, and ensure you have sufficient file system permissions.'
);
throw error;
} finally {
// Clean up archive even if error occurs
if (fs.existsSync(archivePath)) {
try {
fs.unlinkSync(archivePath);
} catch (cleanupError) {
logger.warn('Failed to clean up archive file:', (cleanupError as Error).message);
}
}
}
}

Expand All @@ -110,11 +149,6 @@ export class CKBTui {

static run(args: string[] = []) {
const binaryPath = this.getBinaryPath();
const command = `"${binaryPath}" ${args.join(' ')}`;
return spawnSync(command, { stdio: 'inherit', shell: true });
}

static runWithArgs(args: string[]) {
this.run(args);
return spawnSync(binaryPath, args, { stdio: 'inherit' });
}
}
Loading