Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2e7628b
feat(devnet): add interactive config editor with non-interactive --se…
RetricSu Feb 26, 2026
6e5f9d7
feat(devnet): migrate config tui to blessed with full key browser
RetricSu Feb 26, 2026
24593ae
feat(devnet): add search/add/delete interactions to config TUI
RetricSu Feb 26, 2026
d082480
feat(devnet): add array insert/move and search match navigation
RetricSu Feb 26, 2026
34a549e
fix(devnet): sync file pane navigation with key list refresh
RetricSu Feb 26, 2026
0fc3ed2
feat(devnet): redesign config tui with inline docs and fixed-array ch…
RetricSu Feb 26, 2026
e1c0117
feat(devnet): compact fixed arrays and edit from array row
RetricSu Feb 26, 2026
8e3d651
refactor(tui): decompose 1220-line monolith into focused modules\n\nB…
RetricSu Feb 26, 2026
6e9a5d7
style(tui): tune inline doc comment contrast and stabilize tag rendering
RetricSu Feb 26, 2026
8e2449e
feat(tui): improve section layout and fixed-array presentation
RetricSu Feb 27, 2026
688ad0d
feat(tui): refine section rendering and array-list presentation
RetricSu Feb 27, 2026
73b89fc
fix(tui): stabilize modal/quit keyboard flow and confirm UX
RetricSu Feb 27, 2026
59814e2
refactor(tui): deduplicate confirm dialog keybinding logic
RetricSu Feb 27, 2026
c40ad59
refactor(devnet): move config editor module to src/devnet
RetricSu Feb 27, 2026
74a5808
refactor(cli): use ES import for devnet config command
RetricSu Feb 27, 2026
aaa6d48
style(tui): apply formatting updates across devnet editor modules
RetricSu Feb 27, 2026
01f6fa3
fix(tui): resolve unused vars in format rendering
RetricSu Feb 27, 2026
2fe0763
fix(tui): force xterm terminal profile for blessed compatibility
RetricSu Feb 27, 2026
b1d876e
fix(tui): preserve custom array values and add save-time config valid…
RetricSu Feb 27, 2026
8ad2427
chore(tui): help text
RetricSu Feb 27, 2026
ee2526f
feat(tui): add three-column devnet config editor with readonly refere…
RetricSu Feb 27, 2026
f37ee2e
chore(tui)
RetricSu Feb 27, 2026
48cf736
chore(tui)
RetricSu Feb 27, 2026
1a41c58
re-generate pnpm-lock file
RetricSu Feb 27, 2026
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
42 changes: 36 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,38 @@ offckb system-scripts --output <output-file-path>

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

1. Locate your Devnet config folder:

1. Open the interactive Devnet config editor:

```sh
offckb devnet config
```

The editor uses a three-column layout: first-column file switcher (`ckb.toml` / `ckb-miner.toml`), a middle primary editing pane, and a smaller right read-only reference pane that shows the full built-in template for the currently selected file.

The left editing pane supports full key browsing/editing, including primitive value edits, object key add, array append/insert/move, search filter, and path delete.

Common shortcuts: `Enter` edit primitive, `a` add key/item, `i` insert array item, `m` move array item, `d` delete path, `/` search filter, `n`/`N` next/previous search match, `c` add custom value in fixed-array dialog (when allowed), `s` save, `q` quit.

Note: saving rewrites `ckb.toml` / `ckb-miner.toml` into canonical TOML format; upstream comments and original formatting are not preserved after save.

You can also update the same fields non-interactively (useful for scripts/CI):

```sh
offckb devnet config --set ckb.logger.filter=info
offckb devnet config --set ckb.rpc.enable_deprecated_rpc=true --set miner.client.poll_interval=1500
```

If your terminal is non-interactive (no TTY, e.g. CI/remote pipeline), use `--set` mode directly instead of the full-screen editor.

1. Save changes and restart devnet:

```sh
offckb clean -d
offckb node
```

1. (Advanced) Locate your Devnet config folder for manual edits:

```sh
offckb config list
```
Expand All @@ -293,12 +323,12 @@ Example result:
}
}
```

Pay attention to the `devnet.configPath` and `devnet.dataPath`.

2. `cd` into the `devnet.configPath` . Modify the config files as needed. See [Custom Devnet Setup](https://docs.nervos.org/docs/node/run-devnet-node#custom-devnet-setup) and [Configure CKB](https://github.com/nervosnetwork/ckb/blob/develop/docs/configure.md) for details.
3. After modifications, run `offckb clean -d` to remove the chain data if needed while keeping the updated config files.
4. Restart local blockchain by running `offckb node`

1. `cd` into the `devnet.configPath` . Modify the config files as needed. See [Custom Devnet Setup](https://docs.nervos.org/docs/node/run-devnet-node#custom-devnet-setup) and [Configure CKB](https://github.com/nervosnetwork/ckb/blob/develop/docs/configure.md) for details.
2. After modifications, run `offckb clean -d` to remove the chain data if needed while keeping the updated config files.
3. Restart local blockchain by running `offckb node`

## Config Setting

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
},
"devDependencies": {
"@types/adm-zip": "^0.5.5",
"@types/blessed": "0.1.27",
"@types/jest": "^30.0.0",
"@types/node": "^20.17.24",
"@types/node-fetch": "^2.6.11",
Expand All @@ -80,6 +81,7 @@
"@inquirer/prompts": "^7.8.6",
"@types/http-proxy": "^1.17.15",
"adm-zip": "^0.5.10",
"blessed": "0.1.81",
"chalk": "4.1.2",
"child_process": "^1.0.2",
"ckb-transaction-dumper": "^0.4.2",
Expand Down
28 changes: 28 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { TransferOptions, transfer } from './cmd/transfer';
import { BalanceOption, balanceOf } from './cmd/balance';
import { createScriptProject, CreateScriptProjectOptions } from './cmd/create';
import { Config, ConfigItem } from './cmd/config';
import { devnetConfig } from './cmd/devnet-config';
import { debugSingleScript, debugTransaction, parseSingleScriptOption } from './cmd/debug';
import { printSystemScripts } from './cmd/system-scripts';
import { transferAll } from './cmd/transfer-all';
Expand Down Expand Up @@ -164,6 +165,19 @@ program
.description('do a configuration action')
.action((action, item, value) => Config(action, item as ConfigItem, value));

const devnetCommand = program.command('devnet').description('Devnet utility commands');

devnetCommand
.command('config')
.description('Open a full-screen editor to tweak devnet config files')
.option(
'-s, --set <key=value>',
'Set a devnet config field non-interactively (repeatable), e.g. --set ckb.logger.filter=info',
(value: string, previous: string[] = []) => [...previous, value],
[],
)
.action(devnetConfig);

program.parse(process.argv);

// If no command is specified, display help
Expand Down
79 changes: 79 additions & 0 deletions src/cmd/devnet-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { readSettings } from '../cfg/setting';
import { logger } from '../util/logger';
import { createDevnetConfigEditor } from '../devnet/config-editor';
import { runDevnetConfigTui } from '../tui/devnet-config-tui';

export interface DevnetConfigOptions {
set?: string[];
}

export interface ParsedSetItem {
key: string;
value: string;
}

export function parseSetItem(item: string): ParsedSetItem {
const separator = item.indexOf('=');
if (separator <= 0 || separator === item.length - 1) {
throw new Error(`Invalid --set item '${item}'. Use key=value format.`);
}

const key = item.slice(0, separator).trim();
const value = item.slice(separator + 1).trim();
if (!key || !value) {
throw new Error(`Invalid --set item '${item}'. Key and value must not be empty.`);
}

return { key, value };
}

export function applySetItems(editor: ReturnType<typeof createDevnetConfigEditor>, items: string[]): ParsedSetItem[] {
const parsedItems = items.map(parseSetItem);
for (const parsedItem of parsedItems) {
editor.setFieldValue(parsedItem.key, parsedItem.value);
}
editor.save();
return parsedItems;
}

export async function devnetConfig(options: DevnetConfigOptions = {}) {
const settings = readSettings();
const configPath = settings.devnet.configPath;

try {
const editor = createDevnetConfigEditor(configPath);

if (options.set && options.set.length > 0) {
const parsedItems = applySetItems(editor, options.set);
logger.success(`Devnet config updated at: ${configPath}`);
logger.info(
`Applied ${parsedItems.length} setting(s): ${parsedItems.map((item) => `${item.key}=${item.value}`).join(', ')}`,
);
logger.info('Restart devnet to apply changes: offckb clean -d && offckb node');
return;
}

if (!process.stdin.isTTY || !process.stdout.isTTY) {
logger.error('Interactive devnet config editor requires a TTY terminal.');
logger.info('Use non-interactive mode instead, e.g.:');
logger.info(' offckb devnet config --set ckb.logger.filter=info');
logger.info(' offckb devnet config --set miner.client.poll_interval=1500');
process.exitCode = 1;
return;
}

const isSaved = await runDevnetConfigTui(editor, configPath);

if (isSaved) {
logger.success(`Devnet config updated at: ${configPath}`);
logger.info('Restart devnet to apply changes: offckb clean -d && offckb node');
return;
}

logger.info('No changes saved.');
} catch (error) {
logger.error((error as Error).message);
logger.info('Tip: run `offckb node` once to initialize devnet config files first.');
process.exitCode = 1;
}
}
Loading
Loading