Dfx canister install <CANISTER_NAME> --always-assist throws error in child process

Hello everyone,

I am trying to use --always-assist functionality in a node.js application, my goal is create a interactive season within node.js and show questions to user and get inputs from them and send back to the dfx as an answer.

But test code fails, command even works with dfx, not worked in same way with spawn process

Can someone help me to understand problem, the line that started with Installling code... catching as stderr, but why?

I am testing dfx canister install <CANISTER_NAME> --always-assist

Here is my test code

import { spawn } from 'child_process';
import readline from 'readline';

const canisterName = 'topup_frontend'; // Replace with your actual canister name

const dfxProcess = spawn('dfx', ['canister', 'install', canisterName, '--always-assist'], {
  stdio: ['pipe', 'pipe', 'pipe']
});

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

// Handle stdout
dfxProcess.stdout.on('data', (data) => {
  const output = data.toString();
  console.log('Stdout:', output);

  // Check if the output is asking for input
  if (output.includes('?')) {
    rl.question('Input required: ', (answer) => {
      dfxProcess.stdin.write(answer + '\n');
    });
  }
});

// Handle stderr
dfxProcess.stderr.on('data', (data) => {
  console.error('Stderr:', data.toString());
});

// Handle process exit
dfxProcess.on('close', (code) => {
  console.log(`Child process exited with code ${code}`);
  rl.close();
});

rl.on('close', () => {
  console.log("Interactive session ended");
  process.exit(0);
});

Output of Test Code

Output of dfx

Install Command Details: sdk/src/dfx/src/commands/canister/install.rs at master · dfinity/sdk · GitHub

@chenyan might have some insights into this problem or how to fix

1 Like

The assist feature is only enabled when running in terminal: sdk/src/dfx/src/util/mod.rs at master · dfinity/sdk · GitHub. So my guess is that by spawning the command in node, it doesn’t have a terminal attached to it.

1 Like

Thanks @chenyan,

Actually I tried solutions like with shell: true and stdio: inherit options seperately in spawn process. I can use stdio: inherit to success that but I lose the ability to programmatically handle input/output. I need to use pipe handle input/output programmatically.

I am not sure why is_terminal must on this method, couldn’t be use without is_terminal inside of dfx?

In summary, even if I use shell: true, is_terminal returns false with pipe, and inherit returns true, but I think I should be able to access it both ways.

End of the day my purpose is catch the questions (in realtime), show the user with an interface in electron app and get an answer, send that answer to process.

So if you have any kind of suggestion for this problem or another approach to success that, would be appreciated.

I put the is_terminal check there mainly to prevent people accidentally running this in CI and get stuck. Maybe with the --always-assist flag, we can relax that.

OTOH, the most clean way is to use the candid_parser library directly and you can handle the interaction without relying on dfx.