Skip to content
Draft
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
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"scripts": {
"build": "turbo run build --log-prefix=none",
"test": "turbo run test --log-prefix=none",
"test:integration": "yarn vitest run --config tests/integrations/vitest.config.ts",
"env:up": "make -C tests/integrations env-up",
"env:down": "make -C tests/integrations env-down",
"lint": "biome check .",
"lint:fix": "biome check . --write",
"lint:ci": "biome ci . --no-errors-on-unmatched",
Expand All @@ -17,10 +20,18 @@
},
"devDependencies": {
"@biomejs/biome": "2.3.8",
"@openzeppelin/compact-deployer": "workspace:^",
"@types/node": "24.10.1",
"pino": "^9.7.0",
"ts-node": "^10.9.2",
"turbo": "^2.6.1",
"typescript": "^5.9.3",
"vitest": "^4.0.15"
},
"resolutions": {
"@midnight-ntwrk/ledger-v8": "8.0.3",
"undici": "^6.24.0",
"glob": "^11.0.0",
"uuid": "^13.0.0"
}
}
18 changes: 13 additions & 5 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
{
"name": "@openzeppelin/compact-cli",
"description": "CLI for compiling and building Compact smart contracts",
"description": "CLI for compiling, building, and deploying Compact smart contracts",
"version": "0.0.1",
"keywords": [
"compact",
"cli",
"compiler",
"builder",
"deployer",
"testing"
],
"author": "OpenZeppelin Community <maintainers@openzeppelin.org>",
"license": "MIT",
"type": "module",
"exports": {
"./run-builder": "./dist/runBuilder.js",
"./run-compiler": "./dist/runCompiler.js"
"./run-compiler": "./dist/runCompiler.js",
"./run-deploy": "./dist/runDeploy.js"
},
"files": [
"dist",
"README.md",
"LICENSE"
],
"engines": {
"node": ">=20"
"node": ">=24"
},
"bin": {
"compact-builder": "dist/runBuilder.js",
"compact-compiler": "dist/runCompiler.js"
"compact-compiler": "dist/runCompiler.js",
"compact-deploy": "dist/runDeploy.js"
},
"scripts": {
"build": "tsc -p .",
Expand All @@ -37,12 +40,17 @@
"devDependencies": {
"@tsconfig/node24": "^24.0.3",
"@types/node": "24.10.1",
"@types/ws": "^8.5.10",
"typescript": "^5.9.3",
"vitest": "^4.0.15"
},
"dependencies": {
"@openzeppelin/compact-builder": "workspace:^",
"@openzeppelin/compact-deployer": "workspace:^",
"chalk": "^5.6.2",
"ora": "^9.0.0"
"ora": "^9.0.0",
"pino": "^9.7.0",
"pino-pretty": "^13.0.0",
"ws": "^8.16.0"
}
}
68 changes: 68 additions & 0 deletions packages/cli/src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { mkdirSync } from 'node:fs';
import { join } from 'node:path';
import pino, { type Logger } from 'pino';

/**
* Pino logger factory tuned for the CLI's three modes.
*
* --json : raw JSON to stdout, no transports (CI-friendly).
* default : pretty-printed `info+` to stdout via `pino-pretty`.
* --verbose (no json): same pretty stdout AND `debug+` mirrored to a
* timestamped file under `.compact/logs/` so the
* transcript survives ephemeral spinner overwrites.
*/
export interface CreateLoggerOptions {
verbose: boolean;
json: boolean;
logDir?: string;
}

export function createLogger(opts: CreateLoggerOptions): Logger {
if (opts.json) {
return pino({ level: opts.verbose ? 'debug' : 'info' });
}

if (opts.verbose) {
const dir = opts.logDir ?? join(process.cwd(), '.compact', 'logs');
mkdirSync(dir, { recursive: true });
const file = join(
dir,
`${new Date().toISOString().replace(/[:.]/g, '-')}.log`,
);
return pino(
{ level: 'debug' },
pino.transport({
targets: [
{
target: 'pino/file',
options: { destination: file },
level: 'debug',
},
{
target: 'pino-pretty',
options: {
destination: 1,
colorize: true,
translateTime: 'HH:MM:ss',
ignore: 'pid,hostname',
},
level: 'info',
},
],
}),
);
}

return pino(
{ level: 'info' },
pino.transport({
target: 'pino-pretty',
options: {
destination: 1,
colorize: true,
translateTime: 'HH:MM:ss',
ignore: 'pid,hostname',
},
}),
);
}
56 changes: 56 additions & 0 deletions packages/cli/src/prompt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { stdin, stdout } from 'node:process';

/**
* Prompt the user for a keystore passphrase on stdout and read it from stdin
* with terminal echo suppressed.
*
* Uses raw mode + manual byte handling so we can swallow each character as
* it arrives (no glyphs, no asterisks) and handle Ctrl-C / Backspace
* correctly. Falls back to plain line-read when stdin is not a TTY (piped
* input in CI).
*/
export async function promptPassphrase(label: string): Promise<string> {
stdout.write(`Passphrase for ${label}: `);
return readMaskedLine();
}

function readMaskedLine(): Promise<string> {
return new Promise((resolveFn, rejectFn) => {
let buffer = '';
const isTTY = stdin.isTTY === true;

const cleanup = () => {
if (isTTY) stdin.setRawMode(false);
stdin.pause();
stdin.removeListener('data', onData);
stdout.write('\n');
};

const onData = (chunk: Buffer) => {
const s = chunk.toString('utf8');
for (const ch of s) {
const code = ch.charCodeAt(0);
if (code === 0x03) {
cleanup();
rejectFn(new Error('Aborted'));
return;
}
if (code === 0x0d || code === 0x0a) {
cleanup();
resolveFn(buffer);
return;
}
if (code === 0x7f || code === 0x08) {
buffer = buffer.slice(0, -1);
continue;
}
buffer += ch;
}
};

if (isTTY) stdin.setRawMode(true);
stdin.resume();
stdin.setEncoding('utf8');
stdin.on('data', onData);
});
}
Loading
Loading