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
5 changes: 5 additions & 0 deletions .changeset/ckb-tui-v0.1.4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@offckb/cli': patch
---

Bump the bundled ckb-tui from v0.1.3 to v0.1.4 for `offckb status`. The new release fixes a divide-by-zero panic in ckb-tui's data-sync thread when the connected node has no peers (Officeyutong/ckb-tui#13) — the normal state of a single-node devnet — which permanently froze the Overview, Mempool, Peers, and Blockchain panels within seconds of opening the TUI. SHA-256 digests for the v0.1.4 release assets are pinned in offckb, so the download stays verifiable.
5 changes: 5 additions & 0 deletions .changeset/logs-command-quiet-node.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@offckb/cli': minor
---

Unify devnet logging around the node's log files and add `offckb logs`. A foreground `offckb node` no longer relays the raw node/miner stdout: the console now shows lifecycle events, live contract script debug output (`debug!` in scripts, streamed over the node's TCP log subscription), submitted transaction hashes, and RPC errors — the full node log stays in `data/logs/run.log` as always, and `--verbose` restores the old firehose. The new `offckb logs [node|script|miner|rpc] [-f] [--grep] [--tail]` command reads those log files (`docker logs` style), so logs are reachable in every run mode — foreground, daemon, or while `offckb status` is attached — and pipe/agent friendly. The RPC proxy is quieter too: per-request lines moved from info to debug, JSON-RPC errors in responses now surface as warnings, and everything the proxy sees is appended to `data/logs/proxy.log` (viewable via `offckb logs rpc`).
5 changes: 5 additions & 0 deletions .changeset/terminal-rpc-version-compat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@offckb/cli': patch
---

Fix devnet startup crashing for CKB binaries older than v0.205.0. The devnet ckb.toml template enables the `Terminal` RPC module, which only exists since CKB v0.205.0; older binaries abort at startup with an opaque serde "unknown variant" error, and the legacy-config migration re-added the module on every `offckb node` start even after users removed it by hand. offckb now adapts the devnet config to the CKB version: fresh chains for an old binary are initialized without `Terminal` (the migration also stops re-adding it, while still enabling `tcp_listen_address`), a config that already has `Terminal` paired with an old binary fails fast with an actionable error instead of the serde dump, and an unprobeable custom `--binary-path` that crashes with the tell-tale "unknown variant `Terminal`" message now gets a hint pointing at the cause. The `offckb status` system-metric panels require CKB >= 0.205.0; the README's `status` section says so.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Patch Changes

- 406415e: Change the default devnet log filter from `warn,ckb-script=debug` to `info,ckb-script=debug` in the `ckb.toml` / `ckb-miner.toml` templates (and the config editor's embedded reference templates). A healthy devnet produces almost no `warn`-level output, which left the `offckb status` Logs panel permanently empty and looked broken; `info` keeps the per-block log stream visible while `ckb-script=debug` still surfaces script execution details. Applies to newly initialized chains — edit `[logger] filter` in your existing devnet `ckb.toml` to opt in.

- 699a850: Fix the `status` command showing missing data on devnet: enable the Terminal RPC module and the TCP listen address in the devnet `ckb.toml` template (and the config editor's embedded reference template), and pass the node's TCP listen address to ckb-tui so the system metrics, mempool, and log panels populate correctly. The devnet RPC now binds to `127.0.0.1` instead of `0.0.0.0` so the unauthenticated RPC (including the new host metrics) is no longer reachable from other machines on the network; edit `rpc.listen_address` in the devnet `ckb.toml` if you rely on remote access.
- 45b0e98: Rename `--allow-mainnet-replay-risk` to `--allow-external-key-on-mainnet-fork` (#460) — the old flag remains as a hidden deprecated alias so existing scripts keep working — and apply the fixes left over from the 0.4.9 review (#462):

Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Commands:
balance [options] [toAddress] Check account balance, only devnet and testnet
debugger Port of the raw CKB Standalone Debugger
status [options] Show ckb-tui status interface
logs [options] [target] Show devnet logs: node (default), contract script debug output, miner, or RPC proxy events
config <action> [item] [value] do a configuration action
devnet config Edit devnet configuration
devnet info Show fork metadata and node/indexer readiness
Expand Down Expand Up @@ -144,6 +145,21 @@ Stop the daemon later with:
offckb node stop
```

**View Logs**

A foreground `offckb node` stays quiet by default: it prints lifecycle events, contract script debug output (`debug!` in your scripts), submitted transaction hashes, and RPC errors. The node, miner, and RPC proxy always write full logs to files under the devnet data folder, and `offckb logs` reads them in any run mode (foreground, daemon, or while `offckb status` is attached):

```sh
offckb logs # node log (default)
offckb logs script # contract script debug output only
offckb logs miner # miner log
offckb logs rpc # RPC requests, transaction hashes, RPC errors
offckb logs -f # stream new lines, tail -f style
offckb logs --tail 200 --grep ERROR
```

Use `offckb node --verbose` to restore the old behavior of printing the full raw node/miner output to the terminal.

**Agent-Friendly JSON Output**

For programmatic consumption or agent integration, add `--json` before or after the command:
Expand Down Expand Up @@ -188,6 +204,8 @@ offckb status --network mainnet

`status` performs a JSON-RPC health check through the proxy before opening the TUI and requires an interactive terminal.

The TUI's system-metric panels are powered by CKB's `Terminal` RPC module, which requires CKB >= 0.205.0. If you run the devnet with an older CKB (e.g. `offckb node 0.120.0` or `--binary-path` pointing at an old build), offckb starts the node without that module and those panels will be unavailable; upgrade CKB to get them.

### 2. Create a New Contract Project {#create-project}

Generate a ready-to-use smart-contract project in JS/TS using templates:
Expand Down Expand Up @@ -331,7 +349,7 @@ offckb system-scripts --output <output-file-path>

### 6. Tweak Devnet Config {#tweak-devnet-config}

By default, OffCKB use a fixed Devnet config. You can customize it, for example by modifying the default log level (`warn,ckb-script=debug`).
By default, OffCKB use a fixed Devnet config. You can customize it, for example by modifying the default log level (`info,ckb-script=debug`).

1. Open the interactive Devnet config editor:

Expand Down
2 changes: 1 addition & 1 deletion ckb/devnet/ckb-miner.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ data_dir = "data"
spec = { file = "specs/dev.toml" }

[logger]
filter = "warn,ckb-script=debug"
filter = "info,ckb-script=debug"
color = true
log_to_file = true
log_to_stdout = true
Expand Down
2 changes: 1 addition & 1 deletion ckb/devnet/ckb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ data_dir = "data"
spec = { file = "specs/dev.toml" }

[logger]
filter = "warn,ckb-script=debug"
filter = "info,ckb-script=debug"
color = true
log_to_file = true
log_to_stdout = true
Expand Down
2 changes: 1 addition & 1 deletion src/cfg/setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const defaultSettings: Settings = {
minVersion: '0.200.0',
},
ckbTui: {
version: 'v0.1.3',
version: 'v0.1.4',
},
},
};
Expand Down
41 changes: 37 additions & 4 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env node
import { Command, CommanderError, Option } from 'commander';
import { Command, CommanderError, InvalidArgumentError, Option, Argument } from 'commander';
import { startNode, stopNode } from './cmd/node';
import { accounts } from './cmd/accounts';
import { clean } from './cmd/clean';
Expand All @@ -15,6 +15,7 @@ import { devnetConfig } from './cmd/devnet-config';
import { devnetFork } from './cmd/devnet-fork';
import { devnetInfo } from './cmd/devnet-info';
import { debugSingleScript, debugTransaction, parseSingleScriptOption } from './cmd/debug';
import { logsCommand, LogsOptions } from './cmd/logs';
import { printSystemScripts } from './cmd/system-scripts';
import { transferAll } from './cmd/transfer-all';
import { genSystemScriptsJsonFile } from './scripts/gen';
Expand Down Expand Up @@ -76,15 +77,47 @@ const nodeCommand = program
'Specify the CKB binary path to use, only for devnet, when set, will ignore version and network',
)
.option('--daemon', 'Run the node in the background as a daemon (devnet only)')
.action(async (version: string, options: { network: Network; binaryPath?: string; daemon?: boolean }) => {
return startNode({ version, network: options.network, binaryPath: options.binaryPath, daemon: options.daemon });
});
.option(
'--verbose',
'Print the full raw node/miner output (default shows lifecycle events, script output, tx hashes, and RPC errors)',
)
.action(
async (
version: string,
options: { network: Network; binaryPath?: string; daemon?: boolean; verbose?: boolean },
) => {
return startNode({
version,
network: options.network,
binaryPath: options.binaryPath,
daemon: options.daemon,
verbose: options.verbose,
});
},
);

nodeCommand
.command('stop')
.description('Stop the running CKB devnet daemon')
.action(async () => stopNode());

program
.command('logs')
.description('Show devnet logs: node (default), contract script debug output, miner, or RPC proxy events')
.addArgument(
new Argument('[target]', 'Which logs to show').choices(['node', 'script', 'miner', 'rpc']).default('node'),
)
.option('-f, --follow', 'Stream new log lines as they are written (like tail -f)')
.option('--grep <pattern>', 'Only show lines containing the given text')
.option('--tail <lines>', 'Show the last N lines before following', (value: string) => {
const parsed = Number(value);
if (!Number.isInteger(parsed) || parsed < 0) {
throw new InvalidArgumentError('--tail must be a non-negative integer');
}
return parsed;
})
.action((target: string, options: LogsOptions) => logsCommand(target, options));

program
.command('create [project-name]')
.description('Create a new CKB Smart Contract project in JavaScript.')
Expand Down
57 changes: 57 additions & 0 deletions src/cmd/logs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
LOG_TARGETS,
LogTarget,
SCRIPT_LOG_TARGET,
filterLinesByTarget,
followLogFile,
grepLines,
parseCkbLogLine,
readLogTail,
resolveLogPath,
} from '../devnet/log-file';
import { readSettings, Settings } from '../cfg/setting';
import { logger as defaultLogger, UnifiedLogger } from '../util/logger';

export interface LogsOptions {
follow?: boolean;
grep?: string;
tail?: number;
}

const DEFAULT_TAIL = 100;

/**
* Print (and optionally follow) a devnet log file. The core is synchronous so
* it can be unit tested with temp files; --follow hands control to
* followLogFile and keeps the process alive until Ctrl-C.
*/
export function showLogs(target: LogTarget, options: LogsOptions, settings: Settings, logger: UnifiedLogger): void {
const filePath = resolveLogPath(target, settings);
const tail = options.tail ?? DEFAULT_TAIL;

let lines = readLogTail(filePath, tail);
const scriptOnly = target === 'script';
if (scriptOnly) lines = filterLinesByTarget(lines, SCRIPT_LOG_TARGET);
if (options.grep) lines = grepLines(lines, options.grep);
for (const line of lines) logger.info(line);

if (!options.follow) return;

let inScriptEntry = false;
followLogFile(filePath, (line) => {
let show = true;
if (scriptOnly) {
// Unparsable lines are continuations of the previous entry.
const parsed = parseCkbLogLine(line);
if (parsed) inScriptEntry = parsed.target === SCRIPT_LOG_TARGET;
show = inScriptEntry;
}
if (show && options.grep && !line.includes(options.grep)) show = false;
if (show) logger.info(line);
});
}

export function logsCommand(target: string | undefined, options: LogsOptions): void {
const resolved: LogTarget = LOG_TARGETS.includes(target as LogTarget) ? (target as LogTarget) : 'node';
showLogs(resolved, options, readSettings(), defaultLogger);
}
Loading
Loading