Implement ChenAIKit CLI commands and shared config utilities#130
Conversation
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR implements the complete CLI command framework for ChenAIKit, adding four command modules (account, transaction, credit-score, fraud) with interactive prompts, progress tracking, and error handling. It introduces a persistent configuration system for storing account profiles and settings, migrates entry point logic to distribution-based execution, and updates dependencies to support Stellar SDK integration. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant CLI as CLI Entry<br/>(index.ts)
participant Cmd as Command<br/>(e.g., account.ts)
participant Config as Config<br/>System
participant Stellar as StellarConnector<br/>or Core API
participant UI as UI/Spinner<br/>(ora, chalk)
User->>CLI: run command (e.g., account create)
CLI->>CLI: Parse args, register commands
CLI->>Cmd: Execute command handler
Cmd->>Config: Load config
Config-->>Cmd: Return config
Cmd->>UI: Show prompt/spinner
User->>Cmd: Provide input (via inquirer)
Cmd->>Cmd: Validate input
Cmd->>Stellar: Call API (e.g., fetch balance)
Stellar-->>Cmd: Return result
Cmd->>Config: Save/Update profile
Config-->>Cmd: Config updated
Cmd->>UI: Display result (colorized)
UI-->>User: Show output
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (3)
packages/cli/src/commands/fraud.ts (2)
97-99: Consider removing the artificial delay.The
await wait(200)adds unnecessary latency before the actual scoring call. If the intent is to ensure the spinner is visible, consider moving it after scoring for slow responses, or removing it entirely since the real operation will have its own latency.💡 Suggested removal
try { - await wait(200); const result = await detector.score(transaction);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cli/src/commands/fraud.ts` around lines 97 - 99, Remove the artificial delay by deleting the await wait(200) call before calling detector.score(transaction) in the command handler; if you need to guarantee a visible spinner for very fast responses, instead start the spinner, call detector.score(transaction) immediately, and only show a minimal delay after scoring (or conditionally show the spinner for responses under a threshold) so the real latency from detector.score is preserved.
103-123: RedundantMath.min(100, riskScore)on line 119.Line 106 already clamps the score to 100, and then lines 107-118 may add more points. The second
Math.minon line 119 is necessary, but the first one on line 106 is redundant since you add to it afterward anyway.♻️ Simplified heuristic calculation
} catch (error) { fallback = true; spinner.warn('Realtime fraud model unavailable, using heuristic scoring'); - riskScore = Math.min(100, Math.round(transaction.amount / 20 + (transaction.channel === 'online' ? 25 : 0))); + riskScore = Math.round(transaction.amount / 20 + (transaction.channel === 'online' ? 25 : 0)); if (transaction.country && !['US', 'CA', 'GB', 'DE'].includes(transaction.country.toUpperCase())) { riskScore += 10; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cli/src/commands/fraud.ts` around lines 103 - 123, The heuristic fallback clamps riskScore twice: riskScore is set with Math.min(100, ...) then later adjusted—remove the initial redundant clamp and only apply Math.min(100, riskScore) after all adjustments so final value is clamped once; update the block around the riskScore assignment (the fallback branch using variables riskScore, transaction, reasons, fallback and spinner.warn) to initialize riskScore without Math.min and keep the final Math.min(100, riskScore) as the single clamp.packages/cli/src/index.ts (1)
53-58: Debug flag may not be available when parsing fails early.If
parseAsyncthrows before argument parsing completes (e.g., due to a malformed input or missing subcommand),program.opts()may not contain the--debugflag value. Consider defaulting tofalseexplicitly or catching this edge case.💡 Suggested improvement
try { await program.parseAsync(argv); } catch (error) { - const opts = program.opts<{ debug?: boolean }>(); - handleCliError(error, Boolean(opts.debug)); + let debug = false; + try { + const opts = program.opts<{ debug?: boolean }>(); + debug = Boolean(opts.debug); + } catch { + // opts not available if parsing failed early + } + handleCliError(error, debug); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cli/src/index.ts` around lines 53 - 58, When parseAsync can throw before options are populated, calling program.opts() may not yield the --debug flag; update the catch to determine debug safely and pass it to handleCliError: use a safe fallback (e.g., default debug = false) or derive debug from argv if present, then call handleCliError(error, debug). Reference program.parseAsync, program.opts, handleCliError and argv when locating where to apply this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/cli/bin/chenaikit.js`:
- Line 2: The launcher currently only requires the compiled module so the
module's if (require.main === module) path never runs; change the launcher to
import (or require) the exported run function from '../dist/index.js' and invoke
run() directly (i.e., call the run export from the required module) so the CLI
initialization and command parsing execute even when the module isn't the main
module; reference the run export from the compiled module and invoke it in the
launcher.
In `@packages/cli/src/commands/account.ts`:
- Around line 94-117: The progress messages and log text around account setup
incorrectly imply the secret is encrypted/secure while code stores the raw seed
in profile.secretKey (see AccountProfile, secretKey, saveSecret) and later
persists it as JSON; update the UI text (the steps array and any "Stored
securely in config" messages) to accurately state that the secret is being saved
in plaintext or, if you intend true encryption, implement encryption before
assigning profile.secretKey and persisting it (e.g., wrap secret with an
encryptSecret(...) call and store the ciphertext instead); ensure any messaging
and variable names match the chosen approach so you do not claim encryption when
storing the raw seed.
- Around line 113-119: When using a saved AccountProfile, use the
profile.network value instead of options.network/config.network when resolving
the connector and resolvedAddress; update the code that builds resolvedAddress
and any later connector resolution to prefer profile.network (falling back to
options.network then config.network) so saved/default accounts are queried
against their recorded network. Apply the same change to the other occurrence
where a saved/default account is used (the other resolvedAddress/connector
resolution path) so both places honor AccountProfile.network.
In `@packages/cli/src/commands/credit-score.ts`:
- Around line 123-145: The current single try around both scorer.calculateScore
and scorer.getScoreFactors causes a fallbackScore to overwrite a successful
model score when only factor lookup fails; split them into two try/catch blocks:
first call scorer.calculateScore({...}) (keep spinner.succeed and set
fallbackUsed = true and fallbackScore/fallbackReasons only in this catch), then
in a separate try call scorer.getScoreFactors({...}) and on its catch set
reasons = fallbackReasons(inputs) and log/warn the error but do NOT overwrite
score or set fallbackUsed; reference calculateScore, getScoreFactors,
fallbackScore, fallbackReasons, fallbackUsed, and spinner when making the
change.
In `@packages/cli/src/commands/transaction.ts`:
- Around line 115-117: The code creates a Keypair from sourceSecret via
Keypair.fromSecret and then discards the secret, but the outgoing payload only
includes sourcePublicKey and metadata so no authorized/signed transaction is
produced; update the send flow to either sign the transaction before dropping
the secret (e.g., build the Transaction object and call sign with the Keypair to
produce a signedXDR) and attach that signedXDR (or an encrypted/authorized
signing token) to the payload, or explicitly pass the secret material to the
downstream connector if that is intended; look for references to sourceSecret,
sourcePublicKey, Keypair.fromSecret and the payload construction (the block that
adds sourcePublicKey + metadata) and ensure the payload contains a signed
transaction (signed XDR) or an authorized signing artifact instead of only the
public key.
In `@packages/cli/src/utils/config.ts`:
- Around line 7-24: The config types (AccountProfile and ChenaiCliConfig) can
contain secrets (secretKey, apiKey, aiApiKey) but the code that persists the
config currently uses default filesystem modes; update the code that
writes/creates the config file and directory (the routine that serializes
ChenaiCliConfig to disk) to explicitly create the config directory with
restrictive permissions (mkdir mode 0o700) and write the config file with mode
0o600 (or equivalent API options), and ensure any replace/rename flow preserves
those permissions; also harden any temp-file or atomic-write logic to set the
same restrictive permissions so the stored secret fields are not world-readable.
- Around line 17-18: DEFAULT_CONFIG currently hardcodes horizonUrl to testnet
causing mismatches when only network is changed; update the config resolution so
horizonUrl is derived from the effective network. Modify DEFAULT_CONFIG and the
config-loading path that produces the runtime config (the code that reads/merges
network with DEFAULT_CONFIG; look for the constants named DEFAULT_CONFIG and the
fields network and horizonUrl) so that horizonUrl is set conditionally from
SupportedNetwork (e.g., use mainnet Horizon URL when network === 'mainnet',
testnet URL when network === 'testnet') rather than a fixed string; ensure any
helper that returns the final config (the routine assembling/returning the
effective config used by send/balance commands) recomputes horizonUrl from the
resolved network so passing --network actually changes horizonUrl at runtime.
In `@pnpm-workspace.yaml`:
- Around line 8-14: The CI workflows hardcode pnpm version 9 in the
pnpm/action-setup step causing mismatch with the pinned [email protected] in
package.json; open the workflow steps that use the action (the steps using
"pnpm/action-setup@v3") and remove the "version: 9" input so the action will use
the pinned packageManager from package.json automatically (update both
occurrences currently in the frontend and backend workflow files).
---
Nitpick comments:
In `@packages/cli/src/commands/fraud.ts`:
- Around line 97-99: Remove the artificial delay by deleting the await wait(200)
call before calling detector.score(transaction) in the command handler; if you
need to guarantee a visible spinner for very fast responses, instead start the
spinner, call detector.score(transaction) immediately, and only show a minimal
delay after scoring (or conditionally show the spinner for responses under a
threshold) so the real latency from detector.score is preserved.
- Around line 103-123: The heuristic fallback clamps riskScore twice: riskScore
is set with Math.min(100, ...) then later adjusted—remove the initial redundant
clamp and only apply Math.min(100, riskScore) after all adjustments so final
value is clamped once; update the block around the riskScore assignment (the
fallback branch using variables riskScore, transaction, reasons, fallback and
spinner.warn) to initialize riskScore without Math.min and keep the final
Math.min(100, riskScore) as the single clamp.
In `@packages/cli/src/index.ts`:
- Around line 53-58: When parseAsync can throw before options are populated,
calling program.opts() may not yield the --debug flag; update the catch to
determine debug safely and pass it to handleCliError: use a safe fallback (e.g.,
default debug = false) or derive debug from argv if present, then call
handleCliError(error, debug). Reference program.parseAsync, program.opts,
handleCliError and argv when locating where to apply this change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6fa16d35-d055-4535-b214-6cecb572f0b4
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (13)
backend/package.jsonpackages/cli/bin/chenaikit.jspackages/cli/bin/chenaikit.tspackages/cli/package.jsonpackages/cli/src/commands/account.tspackages/cli/src/commands/credit-score.tspackages/cli/src/commands/fraud.tspackages/cli/src/commands/transaction.tspackages/cli/src/index.tspackages/cli/src/utils/config.tspackages/cli/tsconfig.jsonpackages/core/package.jsonpnpm-workspace.yaml
| // TODO: Implement contract generation | ||
| }); | ||
| program.parse(); | ||
| require('../dist/index.js'); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
sed -n '1,20p' packages/cli/bin/chenaikit.js
printf '\n---\n'
sed -n '34,80p' packages/cli/src/index.tsRepository: nexoraorg/chenaikit
Length of output: 1066
🏁 Script executed:
#!/bin/bash
# Check if dist/index.js exists and inspect its exports and the require.main check
echo "=== Checking dist/index.js structure ==="
if [ -f "packages/cli/dist/index.js" ]; then
wc -l packages/cli/dist/index.js
echo -e "\n--- First 50 lines ---"
head -50 packages/cli/dist/index.js
echo -e "\n--- Searching for 'run' export ---"
rg 'exports\.run|module\.exports\.run' packages/cli/dist/index.js -A 2 -B 2
echo -e "\n--- Searching for require.main check ---"
rg 'require\.main' packages/cli/dist/index.js -A 2 -B 2
else
echo "dist/index.js not found"
fi
# Also check if there are any other files that might call run() from the launcher
echo -e "\n=== Checking for other launcher files ==="
find packages/cli -name "*.js" -o -name "*.ts" | grep -E "(bin|index)" | head -20Repository: nexoraorg/chenaikit
Length of output: 344
🏁 Script executed:
#!/bin/bash
echo "=== packages/cli/bin/chenaikit.ts ==="
cat -n packages/cli/bin/chenaikit.ts
echo -e "\n=== packages/cli/src/index.js ==="
wc -l packages/cli/src/index.js
head -60 packages/cli/src/index.js
echo -e "\n=== packages/cli/bin/stellar-ai.js (for comparison) ==="
cat -n packages/cli/bin/stellar-ai.js
echo -e "\n=== Looking for build config or tsconfig ==="
find packages/cli -maxdepth 3 -name "tsconfig.json" -o -name "build.js" -o -name "webpack.config.js" | head -10Repository: nexoraorg/chenaikit
Length of output: 364
🏁 Script executed:
#!/bin/bash
echo "=== packages/cli/tsconfig.json ==="
cat packages/cli/tsconfig.json
echo -e "\n=== Verifying packages/cli/src/index.ts exports ==="
rg 'export.*function run|export.*const run' packages/cli/src/index.ts -A 3 -B 1
echo -e "\n=== Checking if require.main === module exists ==="
rg 'require\.main === module' packages/cli/src/index.ts -A 2 -B 2
echo -e "\n=== Check package.json bin entry ==="
rg '"bin"' packages/cli/package.json -A 5Repository: nexoraorg/chenaikit
Length of output: 960
Call run() from the launcher.
Line 2 only requires the compiled module without invoking it. Because packages/cli/src/index.ts only auto-executes via if (require.main === module) when it is the entry point, this launcher's require() call fails that check. The require.main property points to the launcher, not the required module, so run() never executes and the CLI exits without parsing any commands or registering any handlers.
Suggested fix
- require('../dist/index.js');
+ const { run } = require('../dist/index.js');
+
+ void run(process.argv).catch((error) => {
+ console.error(error);
+ process.exitCode = 1;
+ });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| require('../dist/index.js'); | |
| const { run } = require('../dist/index.js'); | |
| void run(process.argv).catch((error) => { | |
| console.error(error); | |
| process.exitCode = 1; | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/bin/chenaikit.js` at line 2, The launcher currently only
requires the compiled module so the module's if (require.main === module) path
never runs; change the launcher to import (or require) the exported run function
from '../dist/index.js' and invoke run() directly (i.e., call the run export
from the required module) so the CLI initialization and command parsing execute
even when the module isn't the main module; reference the run export from the
compiled module and invoke it in the launcher.
| const steps = [ | ||
| { message: 'Encrypting secret locally', duration: 400 }, | ||
| { message: 'Preparing configuration entry', duration: 300 }, | ||
| { message: 'Finalizing account metadata', duration: 350 }, | ||
| ]; | ||
| const bar = new cliProgress.SingleBar( | ||
| { | ||
| format: ' Account setup |{bar}| {percentage}% | {message}', | ||
| }, | ||
| cliProgress.Presets.shades_classic | ||
| ); | ||
| bar.start(steps.length, 0, { message: steps[0].message }); | ||
| for (let i = 0; i < steps.length; i += 1) { | ||
| const step = steps[i]; | ||
| await wait(step.duration); | ||
| bar.update(i + 1, { message: step.message }); | ||
| } | ||
| bar.stop(); | ||
|
|
||
| const profile: AccountProfile = { | ||
| label, | ||
| publicKey: keypair.publicKey(), | ||
| secretKey: saveSecret ? keypair.secret() : undefined, | ||
| network, |
There was a problem hiding this comment.
Don't describe plaintext seed storage as encrypted or secure.
The seed is written directly into profile.secretKey and later persisted as JSON, yet the setup copy says "Encrypting secret locally" and "Stored securely in config". That wording is inaccurate for a live signing key and encourages unsafe storage assumptions.
Also applies to: 127-128
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/src/commands/account.ts` around lines 94 - 117, The progress
messages and log text around account setup incorrectly imply the secret is
encrypted/secure while code stores the raw seed in profile.secretKey (see
AccountProfile, secretKey, saveSecret) and later persists it as JSON; update the
UI text (the steps array and any "Stored securely in config" messages) to
accurately state that the secret is being saved in plaintext or, if you intend
true encryption, implement encryption before assigning profile.secretKey and
persisting it (e.g., wrap secret with an encryptSecret(...) call and store the
ciphertext instead); ensure any messaging and variable names match the chosen
approach so you do not claim encryption when storing the raw seed.
| const profile: AccountProfile = { | ||
| label, | ||
| publicKey: keypair.publicKey(), | ||
| secretKey: saveSecret ? keypair.secret() : undefined, | ||
| network, | ||
| createdAt: new Date().toISOString(), | ||
| }; |
There was a problem hiding this comment.
Use the saved profile's network for saved/default accounts.
AccountProfile already records network, but when resolvedAddress comes from config you still resolve the connector from options.network/config.network only. A default mainnet account can therefore be queried against testnet unless the user remembers to pass --network every time.
Also applies to: 178-179
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/src/commands/account.ts` around lines 113 - 119, When using a
saved AccountProfile, use the profile.network value instead of
options.network/config.network when resolving the connector and resolvedAddress;
update the code that builds resolvedAddress and any later connector resolution
to prefer profile.network (falling back to options.network then config.network)
so saved/default accounts are queried against their recorded network. Apply the
same change to the other occurrence where a saved/default account is used (the
other resolvedAddress/connector resolution path) so both places honor
AccountProfile.network.
| try { | ||
| await wait(250); | ||
| score = await scorer.calculateScore({ | ||
| address: targetAddress, | ||
| network: options.network ?? config.network, | ||
| telemetry: inputs, | ||
| }); | ||
| reasons = | ||
| (await scorer.getScoreFactors({ | ||
| address: targetAddress, | ||
| network: options.network ?? config.network, | ||
| telemetry: inputs, | ||
| })) ?? []; | ||
| spinner.succeed('Score generated'); | ||
| } catch (error) { | ||
| fallbackUsed = true; | ||
| spinner.warn('Standard model unavailable, using heuristic score'); | ||
| score = fallbackScore(inputs); | ||
| reasons = fallbackReasons(inputs); | ||
| if (error instanceof Error) { | ||
| console.log(chalk.gray(`Reason: ${error.message}`)); | ||
| } | ||
| } |
There was a problem hiding this comment.
Keep a valid model score if only factor lookup fails.
calculateScore() and getScoreFactors() share one try. If the score calculation succeeds but factor retrieval fails, the catch replaces the real score with the heuristic fallback. Split factor retrieval into its own try so partial model output is preserved.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/src/commands/credit-score.ts` around lines 123 - 145, The
current single try around both scorer.calculateScore and scorer.getScoreFactors
causes a fallbackScore to overwrite a successful model score when only factor
lookup fails; split them into two try/catch blocks: first call
scorer.calculateScore({...}) (keep spinner.succeed and set fallbackUsed = true
and fallbackScore/fallbackReasons only in this catch), then in a separate try
call scorer.getScoreFactors({...}) and on its catch set reasons =
fallbackReasons(inputs) and log/warn the error but do NOT overwrite score or set
fallbackUsed; reference calculateScore, getScoreFactors, fallbackScore,
fallbackReasons, fallbackUsed, and spinner when making the change.
| let sourcePublicKey: string; | ||
| try { | ||
| sourcePublicKey = Keypair.fromSecret(sourceSecret).publicKey(); |
There was a problem hiding this comment.
This send flow never produces an authorized transaction.
After Line 117 the secret is discarded, and the payload at Lines 159-166 only contains the source public key plus metadata. The UI says the transaction is being signed, but there is no signed XDR or secret material passed downstream, so this command cannot submit a real payment once the connector is implemented.
Also applies to: 133-166
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/src/commands/transaction.ts` around lines 115 - 117, The code
creates a Keypair from sourceSecret via Keypair.fromSecret and then discards the
secret, but the outgoing payload only includes sourcePublicKey and metadata so
no authorized/signed transaction is produced; update the send flow to either
sign the transaction before dropping the secret (e.g., build the Transaction
object and call sign with the Keypair to produce a signedXDR) and attach that
signedXDR (or an encrypted/authorized signing token) to the payload, or
explicitly pass the secret material to the downstream connector if that is
intended; look for references to sourceSecret, sourcePublicKey,
Keypair.fromSecret and the payload construction (the block that adds
sourcePublicKey + metadata) and ensure the payload contains a signed transaction
(signed XDR) or an authorized signing artifact instead of only the public key.
| export interface AccountProfile { | ||
| label: string; | ||
| publicKey: string; | ||
| secretKey?: string; | ||
| network: SupportedNetwork; | ||
| createdAt: string; | ||
| notes?: string; | ||
| } | ||
|
|
||
| export interface ChenaiCliConfig { | ||
| network: SupportedNetwork; | ||
| horizonUrl: string; | ||
| apiKey?: string; | ||
| aiProvider: 'openai' | 'huggingface' | 'custom'; | ||
| aiApiKey?: string; | ||
| defaultAccount?: string; | ||
| telemetry?: boolean; | ||
| accounts: Record<string, AccountProfile>; |
There was a problem hiding this comment.
Persist this config with secret-safe permissions.
This file can hold secretKey, apiKey, and aiApiKey, but both write paths rely on default filesystem modes. On common umasks that leaves ~/.chenaikit/config.json readable by other local users.
Suggested fix
async function ensureConfigFile(): Promise<void> {
try {
await fs.access(CONFIG_PATH);
} catch (_) {
- await fs.mkdir(CONFIG_DIR, { recursive: true });
- await fs.writeFile(CONFIG_PATH, JSON.stringify(DEFAULT_CONFIG, null, 2), 'utf-8');
+ await fs.mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
+ await fs.writeFile(CONFIG_PATH, JSON.stringify(DEFAULT_CONFIG, null, 2), {
+ encoding: 'utf-8',
+ mode: 0o600,
+ });
}
+ await fs.chmod(CONFIG_DIR, 0o700);
+ await fs.chmod(CONFIG_PATH, 0o600);
}
@@
export async function saveConfig(config: ChenaiCliConfig): Promise<void> {
- await fs.mkdir(CONFIG_DIR, { recursive: true });
- await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
+ await fs.mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
+ await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2), {
+ encoding: 'utf-8',
+ mode: 0o600,
+ });
+ await fs.chmod(CONFIG_DIR, 0o700);
+ await fs.chmod(CONFIG_PATH, 0o600);
}Also applies to: 46-52, 72-74
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/src/utils/config.ts` around lines 7 - 24, The config types
(AccountProfile and ChenaiCliConfig) can contain secrets (secretKey, apiKey,
aiApiKey) but the code that persists the config currently uses default
filesystem modes; update the code that writes/creates the config file and
directory (the routine that serializes ChenaiCliConfig to disk) to explicitly
create the config directory with restrictive permissions (mkdir mode 0o700) and
write the config file with mode 0o600 (or equivalent API options), and ensure
any replace/rename flow preserves those permissions; also harden any temp-file
or atomic-write logic to set the same restrictive permissions so the stored
secret fields are not world-readable.
| network: SupportedNetwork; | ||
| horizonUrl: string; |
There was a problem hiding this comment.
Resolve the Horizon URL from the effective network.
DEFAULT_CONFIG pins horizonUrl to testnet, while the commands only switch network and pass config.horizonUrl through unchanged. On a fresh config, --network mainnet still talks to testnet Horizon, so mainnet balance/send flows are routed to the wrong backend.
Also applies to: 30-36, 117-121
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/src/utils/config.ts` around lines 17 - 18, DEFAULT_CONFIG
currently hardcodes horizonUrl to testnet causing mismatches when only network
is changed; update the config resolution so horizonUrl is derived from the
effective network. Modify DEFAULT_CONFIG and the config-loading path that
produces the runtime config (the code that reads/merges network with
DEFAULT_CONFIG; look for the constants named DEFAULT_CONFIG and the fields
network and horizonUrl) so that horizonUrl is set conditionally from
SupportedNetwork (e.g., use mainnet Horizon URL when network === 'mainnet',
testnet URL when network === 'testnet') rather than a fixed string; ensure any
helper that returns the final config (the routine assembling/returning the
effective config used by send/balance commands) recomputes horizonUrl from the
resolved network so passing --network actually changes horizonUrl at runtime.
pnpm-workspace.yaml
Outdated
| onlyBuiltDependencies: | ||
| - '@prisma/client' | ||
| - '@prisma/engines' | ||
| - bcrypt | ||
| - core-js-pure | ||
| - prisma | ||
| - protobufjs |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== packageManager pins =="
rg -n --glob 'package.json' '"packageManager"\s*:\s*"pnpm@' .
echo
echo "== pnpm setup in CI / docs =="
rg -n -C2 'pnpm/action-setup|setup-node|corepack|packageManager|onlyBuiltDependencies|approve-builds' .Repository: nexoraorg/chenaikit
Length of output: 890
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== CI workflow files =="
fd -e yml -e yaml . .github/workflows 2>/dev/null | head -20
echo
echo "== CI setup for node/pnpm (all potential locations) =="
rg -l 'uses:|run:|pnpm' .github/ 2>/dev/null || echo "No .github found"
echo
echo "== Corepack references =="
rg -n 'corepack' .Repository: nexoraorg/chenaikit
Length of output: 385
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== frontend.yml setup =="
cat -n .github/workflows/frontend.yml | head -50
echo
echo "== backend.yml setup =="
cat -n .github/workflows/backend.yml | head -50
echo
echo "== blockchain.yml setup =="
cat -n .github/workflows/blockchain.yml | head -50Repository: nexoraorg/chenaikit
Length of output: 4396
Align CI pnpm version with package.json pin.
The repo pins [email protected] in package.json, but all CI workflows hardcode version: 9 in pnpm/action-setup. This version mismatch will cause inconsistent installs between local development and CI. Update the CI workflows to use the pinned version from package.json instead of hardcoding version 9:
CI workflow files to update
.github/workflows/frontend.yml(line 30).github/workflows/backend.yml(line 24)
Change from:
- uses: pnpm/action-setup@v3
with:
version: 9To:
- uses: pnpm/action-setup@v3This will use the pinned version from packageManager in package.json automatically.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pnpm-workspace.yaml` around lines 8 - 14, The CI workflows hardcode pnpm
version 9 in the pnpm/action-setup step causing mismatch with the pinned
[email protected] in package.json; open the workflow steps that use the action (the
steps using "pnpm/action-setup@v3") and remove the "version: 9" input so the
action will use the pinned packageManager from package.json automatically
(update both occurrences currently in the frontend and backend workflow files).
|
@Christopherdominic fix conflicts |
Signed-off-by: Christopher Dominic Eze <[email protected]>
|
Fixed |
|
@Christopherdominic please resolve |
Signed-off-by: Christopher Dominic Eze <[email protected]>
|
Resolved |
Summary
Build out the CLI with account creation/balance, transaction send, credit-score calculation, and fraud-detect analysis flows, each using Commander, Inquirer, Ora, and cli-progress for UX polish.
Introduce a shared config manager (~/.chenaikit/config.json) to store accounts, defaults, and Horizon/API settings, plus network resolution helpers.
Wire the new commands into the CLI entrypoint/binary, add typed dependencies (@stellar/stellar-sdk, cli-progress, ora), and modernize the CLI tsconfig.
Install ts-jest in @chenaikit/core and @chenaikit/backend so their Jest presets resolve; document required build-script approvals in pnpm-workspace.yaml.
Testing
pnpm test (core + backend suites)
closes #113
Summary by CodeRabbit