From c40aeeac990bd43b17f2da222d8621939f23f573 Mon Sep 17 00:00:00 2001 From: kevinwang Date: Thu, 5 Mar 2026 10:27:48 -0500 Subject: [PATCH 1/4] WIP: fixed the test command and added a promptSecret to collect the user's password initially --- src/orchestrators/test.ts | 32 ++++++++++++++++++------- src/ui/components/default-component.tsx | 10 ++++++++ src/ui/reporters/default-reporter.tsx | 12 +++++++++- src/ui/reporters/json-reporter.ts | 4 ++++ src/ui/reporters/plain-reporter.ts | 6 +++++ src/ui/reporters/reporter.ts | 2 ++ src/ui/reporters/stub-reporter.ts | 1 + src/ui/store/index.ts | 1 + src/utils/file.ts | 12 ++++++++++ src/utils/spawn.ts | 15 ++++-------- test/orchestrator/mocks/reporter.ts | 4 ++++ 11 files changed, 80 insertions(+), 19 deletions(-) diff --git a/src/orchestrators/test.ts b/src/orchestrators/test.ts index bbc54105..aa688d54 100644 --- a/src/orchestrators/test.ts +++ b/src/orchestrators/test.ts @@ -10,6 +10,7 @@ import { sleep } from '../utils/index.js'; import { spawn, spawnSafe } from '../utils/spawn.js'; import { PlanOrchestrator, PlanOrchestratorResponse } from './plan.js'; import { ValidateOrchestrator } from './validate.js'; +import { FileUtils } from '../utils/file.js'; export interface TestArgs { path?: string; @@ -25,12 +26,14 @@ export const TestOrchestrator = { ctx.subprocessStarted(SubProcessName.TEST_INITIALIZE_AND_VALIDATE); // Perform validation initially to ensure the project is valid - const initializationResult = await PluginInitOrchestrator.run({ ...args, noProgress: true }, new StubReporter()); - await ValidateOrchestrator.run({ existing: initializationResult, noProgress: true }, new StubReporter()); + const initializationResult = await PluginInitOrchestrator.run({ ...args, noProgress: true }, reporter); + await ValidateOrchestrator.run({ existing: initializationResult, noProgress: true }, reporter); ctx.subprocessFinished(SubProcessName.TEST_INITIALIZE_AND_VALIDATE); await this.ensureVmIsInstalled(reporter, args.vmOs); + const password = await reporter.promptSecret('Password needed to copy Codify installation to VM...'); + ctx.subprocessStarted(SubProcessName.TEST_STARTING_VM); const baseVmName = args.vmOs === OS.Darwin ? 'codify-test-vm-macos' : 'codify-test-vm-linux'; const vmName = this.generateVmName(); @@ -49,7 +52,7 @@ export const TestOrchestrator = { console.log('VM has been killed... exiting.') process.exit(1); }) - await sleep(10_000); + await sleep(5_000); await this.waitUntilVmIsReady(vmName); ctx.subprocessFinished(SubProcessName.TEST_STARTING_VM); @@ -58,12 +61,25 @@ export const TestOrchestrator = { // Install codify on the VM // await spawn(`tart exec ${vmName} /bin/bash -c "$(curl -fsSL https://releases.codifycli.com/install.sh)"`, { interactive: true }); const { data: ip } = await spawnSafe(`tart ip ${vmName}`, { interactive: true }); - await spawn(`sshpass -p "admin" scp -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${initializationResult.project.codifyFiles[0]} admin@${ip}:~/codify.jsonc`, { interactive: true }); - if (args.vmOs === OS.Darwin) { - await spawn(`tart exec ${vmName} osascript -e "tell application \\"Terminal\\" to do script \\"cd ~/ && codify apply\\""`, { interactive: true }); - } else { - await spawn(`tart exec ${vmName} gnome-terminal -- bash -c "cd ~/ && codify apply"`, { interactive: true }); + await spawn(`sshpass -p "admin" rsync -avz -e 'ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' /usr/local/lib/codify admin@${ip}:~/codify-lib`, { requiresRoot: true }, undefined, password); + + if (await FileUtils.dirExists('~/.local/share/codify')) { + await spawn(`sshpass -p "admin" rsync -avz -e 'ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' ~/.local/share/codify admin@${ip}:~/.local/share/codify`, { interactive: true }); + } + + try { + await spawn(`tart exec ${vmName} sudo mv /Users/admin/codify-lib /usr/local/lib`, { interactive: true }); + await spawn(`tart exec ${vmName} sudo ln -s /usr/local/lib/codify/bin/codify /usr/local/bin/codify`, { interactive: true }); + await spawn(`sshpass -p "admin" scp -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${initializationResult.project.codifyFiles[0]} admin@${ip}:~/codify.jsonc`, { interactive: true }); + + if (args.vmOs === OS.Darwin) { + await spawn(`tart exec ${vmName} osascript -e "tell application \\"Terminal\\" to do script \\"cd ~/ && codify apply\\""`, { interactive: true }); + } else { + await spawn(`tart exec ${vmName} gnome-terminal -- bash -c "cd ~/ && codify apply"`, { interactive: true }); + } + } catch (e) { + ctx.log(`Error copying files to VM: ${e}`); } ctx.subprocessFinished(SubProcessName.TEST_COPYING_OVER_CONFIGS_AND_OPENING_TERMINAL); diff --git a/src/ui/components/default-component.tsx b/src/ui/components/default-component.tsx index 61faad09..f105a5a1 100644 --- a/src/ui/components/default-component.tsx +++ b/src/ui/components/default-component.tsx @@ -89,6 +89,16 @@ export function DefaultComponent(props: { ) } + { + renderStatus === RenderStatus.SECRET_PROMPT && ( + + {renderData as string} + { + emitter.emit(RenderEvent.PROMPT_RESULT, password); + }}/> + + ) + } { renderStatus === RenderStatus.SUDO_PROMPT && ( diff --git a/src/ui/reporters/default-reporter.tsx b/src/ui/reporters/default-reporter.tsx index 3ccb7e16..f2b4b813 100644 --- a/src/ui/reporters/default-reporter.tsx +++ b/src/ui/reporters/default-reporter.tsx @@ -38,7 +38,7 @@ const ProgressLabelMapping = { [SubProcessName.TEST_INITIALIZE_AND_VALIDATE]: 'Initializing and validating your configs', [SubProcessName.TEST_CHECKING_VM_INSTALLED]: 'Checking if VM is installed', [SubProcessName.TEST_STARTING_VM]: 'Starting VM', - [SubProcessName.TEST_COPYING_OVER_CONFIGS_AND_OPENING_TERMINAL]: 'Copying over configs and opening terminal', + [SubProcessName.TEST_COPYING_OVER_CONFIGS_AND_OPENING_TERMINAL]: 'Copying over configs and opening terminal (if a confirmation dialog appears within the VM, please confirm it.)', [SubProcessName.TEST_USER_CONTINUE_ON_VM]: 'Done setup! Please continue on the VM UI', [SubProcessName.TEST_DELETING_VM]: 'Deleting VM', } @@ -189,6 +189,16 @@ export class DefaultReporter implements Reporter { return password; } + async promptSecret(prompt: string): Promise { + const password = await this.updateStateAndAwaitEvent( + () => this.updateRenderState(RenderStatus.SECRET_PROMPT, prompt), + RenderEvent.PROMPT_RESULT, + ); + + await this.displayProgress(); + return password; + } + displayPlan(plan: Plan): void { this.updateRenderState(RenderStatus.DISPLAY_PLAN, plan) } diff --git a/src/ui/reporters/json-reporter.ts b/src/ui/reporters/json-reporter.ts index acb94cd8..65de70a5 100644 --- a/src/ui/reporters/json-reporter.ts +++ b/src/ui/reporters/json-reporter.ts @@ -47,6 +47,10 @@ export class JsonReporter implements Reporter { throw new Error(`Json reporter error: sudo required for command: ${data.command}. Make sure to preconfigure the sudo password for the Json reporter using --sudoPassword`); } + promptSecret(prompt: string): Promise { + throw new Error('Json reporter error: cannot prompt user for values while using Json reporter. Use a different reporter.'); + } + async promptUserForValues(): Promise { throw new Error('Json reporter error: cannot prompt user for values while using Json reporter. Use a different reporter.'); } diff --git a/src/ui/reporters/plain-reporter.ts b/src/ui/reporters/plain-reporter.ts index b7fc8dc1..9361230e 100644 --- a/src/ui/reporters/plain-reporter.ts +++ b/src/ui/reporters/plain-reporter.ts @@ -106,6 +106,12 @@ export class PlainReporter implements Reporter { return result; } + async promptSecret(prompt: string): Promise { + return new Promise((resolve) => { + this.rl.question(`${prompt} (leave empty if not needed) `, (answer) => resolve(answer)); + }); + } + async displayProgress(): Promise {} async promptInput(prompt: string, error?: string): Promise { diff --git a/src/ui/reporters/reporter.ts b/src/ui/reporters/reporter.ts index f90426ea..6e1bb584 100644 --- a/src/ui/reporters/reporter.ts +++ b/src/ui/reporters/reporter.ts @@ -64,6 +64,8 @@ export interface Reporter { promptSudo(pluginName: string, data: CommandRequestData, secureMode: boolean): Promise; + promptSecret(prompt: string): Promise; + promptUserForValues(resources: Array, promptType: PromptType): Promise; promptPressKeyToContinue(message?: string): Promise; diff --git a/src/ui/reporters/stub-reporter.ts b/src/ui/reporters/stub-reporter.ts index bd4eb32f..8a20d298 100644 --- a/src/ui/reporters/stub-reporter.ts +++ b/src/ui/reporters/stub-reporter.ts @@ -17,6 +17,7 @@ export class StubReporter implements Reporter { async promptConfirmation(message: string): Promise { return true; } async promptOptions(message: string, options: string[]): Promise { throw new Error('Method not implemented.'); } async promptSudo(pluginName: string, data: CommandRequestData): Promise { throw new Error('Method not implemented.'); } + async promptSecret(prompt: string): Promise { throw new Error('Method not implemented.'); } async promptUserForValues(resources: Array, promptType: PromptType): Promise { throw new Error('Method not implemented.'); } async promptPressKeyToContinue(message?: string): Promise {} async displayImportResult(importResult: ImportResult): Promise {} diff --git a/src/ui/store/index.ts b/src/ui/store/index.ts index 21bfa385..da68725f 100644 --- a/src/ui/store/index.ts +++ b/src/ui/store/index.ts @@ -22,6 +22,7 @@ export enum RenderStatus { PROMPT_INPUT, PROMPT_PRESS_KEY_TO_CONTINUE, SUDO_PROMPT, + SECRET_PROMPT, DISPLAY_MESSAGE, } diff --git a/src/utils/file.ts b/src/utils/file.ts index 1af73757..b622da80 100644 --- a/src/utils/file.ts +++ b/src/utils/file.ts @@ -22,6 +22,18 @@ export class FileUtils { } } + static async dirExists(dirPath: string, throwIfExistsButNotFile = true): Promise { + try { + const result = await fs.lstat(path.resolve(dirPath)) + if (throwIfExistsButNotFile && !result.isDirectory()) { + throw new Error(`File found at ${dirPath} instead of a file`) + } + return true; + } catch(e) { + return false; + } + } + static async isDir(fileOrDir: string): Promise { const lstat = await fs.lstat(path.resolve(fileOrDir)) return lstat.isDirectory() diff --git a/src/utils/spawn.ts b/src/utils/spawn.ts index 4f4bd9d2..8a938023 100644 --- a/src/utils/spawn.ts +++ b/src/utils/spawn.ts @@ -37,9 +37,9 @@ export async function spawnSafe(cmd: string, options?: SpawnOptions, pluginName? throw new Error('Password must be specified!'); } - if (cmd.toLowerCase().includes('sudo')) { - throw new Error(`Command must not include sudo. Plugin (${pluginName})`) - } + // if (cmd.toLowerCase().includes('sudo')) { + // throw new Error(`Command must not include sudo. Plugin (${pluginName})`) + // } if (pluginName) { ctx.pluginStdout(pluginName, `Running command: ${options?.requiresRoot ? 'sudo' : ''} ${cmd}` + (options?.cwd ? `(${options?.cwd})` : '')) @@ -65,13 +65,8 @@ export async function spawnSafe(cmd: string, options?: SpawnOptions, pluginName? const initialCols = process.stdout.columns ?? 80; const initialRows = process.stdout.rows ?? 24; - // Mac OS uses -SN instead of -Sn - let command; - if (OsUtils.isMacOS()) { - command = options?.requiresRoot ? `sudo -k >/dev/null 2>&1; sudo -SN <<< "${password}" -E ${ShellUtils.getDefaultShell()} ${options?.interactive ? '-i' : ''} -c "${cmd.replaceAll('\'', '\\\'')}"` : cmd; - } else { - command = options?.requiresRoot ? `sudo -k >/dev/null 2>&1; sudo -S <<< "${password}" -E ${ShellUtils.getDefaultShell()} ${options?.interactive ? '-i' : ''} -c '${cmd.replaceAll('\'', '\\\'')}'` : cmd; - } + const command = options?.requiresRoot ? `sudo -k >/dev/null 2>&1; sudo -S <<< "${password}" -E ${ShellUtils.getDefaultShell()} ${options?.interactive ? '-i' : ''} -c "${cmd.replaceAll('"', '\\"')}"` : cmd; + console.log(command); const args = options?.interactive ? ['-i', '-c', command] : ['-c', command] diff --git a/test/orchestrator/mocks/reporter.ts b/test/orchestrator/mocks/reporter.ts index 50336ec2..5460637a 100644 --- a/test/orchestrator/mocks/reporter.ts +++ b/test/orchestrator/mocks/reporter.ts @@ -89,6 +89,10 @@ export class MockReporter implements Reporter { return ''; } + async promptSecret(prompt: string): Promise { + return ''; + } + async promptUserForValues(resourceInfo: ResourceInfo[], promptType: PromptType): Promise { if (this.config?.promptUserForValues) { return this.config.promptUserForValues(resourceInfo); From c446d8a6059c4102610ec8d9b46086175e29baa2 Mon Sep 17 00:00:00 2001 From: kevinwang Date: Fri, 6 Mar 2026 13:03:32 -0500 Subject: [PATCH 2/4] fix: Selectively sync either /usr/local/lib/codify or ~/.local/share/codify --- src/orchestrators/test.ts | 42 ++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/orchestrators/test.ts b/src/orchestrators/test.ts index aa688d54..def8af1a 100644 --- a/src/orchestrators/test.ts +++ b/src/orchestrators/test.ts @@ -1,16 +1,17 @@ import { OS, SpawnStatus } from 'codify-schemas'; import os from 'node:os'; +import path from 'node:path'; import { PluginInitOrchestrator } from '../common/initialize-plugins.js'; -import { ProcessName, ctx, SubProcessName } from '../events/context.js'; +import { ProcessName, SubProcessName, ctx } from '../events/context.js'; import { Reporter } from '../ui/reporters/reporter.js'; import { StubReporter } from '../ui/reporters/stub-reporter.js'; +import { FileUtils } from '../utils/file.js'; import { sleep } from '../utils/index.js'; import { spawn, spawnSafe } from '../utils/spawn.js'; import { PlanOrchestrator, PlanOrchestratorResponse } from './plan.js'; import { ValidateOrchestrator } from './validate.js'; -import { FileUtils } from '../utils/file.js'; export interface TestArgs { path?: string; @@ -32,15 +33,19 @@ export const TestOrchestrator = { await this.ensureVmIsInstalled(reporter, args.vmOs); - const password = await reporter.promptSecret('Password needed to copy Codify installation to VM...'); - ctx.subprocessStarted(SubProcessName.TEST_STARTING_VM); const baseVmName = args.vmOs === OS.Darwin ? 'codify-test-vm-macos' : 'codify-test-vm-linux'; const vmName = this.generateVmName(); await spawnSafe(`tart clone ${baseVmName} ${vmName}`, { interactive: true }); + // We want to install the latest Codify version which usually exists in ~/.local/share/codify/client/current unless it's not there. + const codifyInstall = (await FileUtils.dirExists('~/.local/share/codify/client/current')) + ? '~/.local/share/codify/client/current' + : '/usr/local/lib/codify'; + // Run this in the background. The user will have to manually exit the GUI to stop the test. - spawnSafe(`tart run ${vmName}`, { interactive: true }) + // We bind mount the codify installation and the codify config directory. We choose not use :ro (read-only) because live changes are not supported in read-only mode. + spawnSafe(`tart run ${vmName} --dir=codify-lib:${codifyInstall}:ro --dir=codify-config:${path.dirname(initializationResult.project.codifyFiles[0])}:ro`, { interactive: true }) .finally(() => { ctx.subprocessFinished(SubProcessName.TEST_USER_CONTINUE_ON_VM); ctx.subprocessStarted(SubProcessName.TEST_DELETING_VM); @@ -52,7 +57,7 @@ export const TestOrchestrator = { console.log('VM has been killed... exiting.') process.exit(1); }) - await sleep(5_000); + await sleep(5000); await this.waitUntilVmIsReady(vmName); ctx.subprocessFinished(SubProcessName.TEST_STARTING_VM); @@ -62,24 +67,15 @@ export const TestOrchestrator = { // await spawn(`tart exec ${vmName} /bin/bash -c "$(curl -fsSL https://releases.codifycli.com/install.sh)"`, { interactive: true }); const { data: ip } = await spawnSafe(`tart ip ${vmName}`, { interactive: true }); - await spawn(`sshpass -p "admin" rsync -avz -e 'ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' /usr/local/lib/codify admin@${ip}:~/codify-lib`, { requiresRoot: true }, undefined, password); - - if (await FileUtils.dirExists('~/.local/share/codify')) { - await spawn(`sshpass -p "admin" rsync -avz -e 'ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' ~/.local/share/codify admin@${ip}:~/.local/share/codify`, { interactive: true }); - } - try { - await spawn(`tart exec ${vmName} sudo mv /Users/admin/codify-lib /usr/local/lib`, { interactive: true }); - await spawn(`tart exec ${vmName} sudo ln -s /usr/local/lib/codify/bin/codify /usr/local/bin/codify`, { interactive: true }); - await spawn(`sshpass -p "admin" scp -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${initializationResult.project.codifyFiles[0]} admin@${ip}:~/codify.jsonc`, { interactive: true }); - - if (args.vmOs === OS.Darwin) { - await spawn(`tart exec ${vmName} osascript -e "tell application \\"Terminal\\" to do script \\"cd ~/ && codify apply\\""`, { interactive: true }); - } else { - await spawn(`tart exec ${vmName} gnome-terminal -- bash -c "cd ~/ && codify apply"`, { interactive: true }); - } - } catch (e) { - ctx.log(`Error copying files to VM: ${e}`); + // Add symlinks to the bind mount locations. + await spawn(`tart exec ${vmName} sudo ln -s /Volumes/My\\ Shared\\ Files/codify-lib/bin/codify /usr/local/bin/codify`, { interactive: true }); + await spawn(`tart exec ${vmName} ln -s /Volumes/My\\ Shared\\ Files/codify-config/${path.basename(initializationResult.project.codifyFiles[0])} /Users/admin/codify.jsonc`, { interactive: true }); + // await spawn(`sshpass -p "admin" scp -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${initializationResult.project.codifyFiles[0]} admin@${ip}:~/codify.jsonc`, { interactive: true }); + + await (args.vmOs === OS.Darwin ? spawn(`tart exec ${vmName} osascript -e "tell application \\"Terminal\\" to do script \\"cd ~ && codify apply\\""`, { interactive: true }) : spawn(`tart exec ${vmName} gnome-terminal -- bash -c "cd ~/ && codify apply"`, { interactive: true })); + } catch (error) { + ctx.log(`Error copying files to VM: ${error}`); } ctx.subprocessFinished(SubProcessName.TEST_COPYING_OVER_CONFIGS_AND_OPENING_TERMINAL); From ca9aa24703d095c5f14dea69daf33b05d8eb4b86 Mon Sep 17 00:00:00 2001 From: kevinwang Date: Fri, 6 Mar 2026 13:41:07 -0500 Subject: [PATCH 3/4] Undo un-needed changes --- src/orchestrators/test.ts | 29 ++++++++++++++++++++++++- src/ui/components/default-component.tsx | 10 --------- src/ui/reporters/default-reporter.tsx | 10 --------- src/ui/reporters/json-reporter.ts | 4 ---- src/ui/reporters/plain-reporter.ts | 6 ----- src/ui/reporters/reporter.ts | 2 -- src/ui/reporters/stub-reporter.ts | 1 - src/ui/store/index.ts | 1 - src/utils/spawn.ts | 8 +++---- test/orchestrator/mocks/reporter.ts | 4 ---- 10 files changed, 31 insertions(+), 44 deletions(-) diff --git a/src/orchestrators/test.ts b/src/orchestrators/test.ts index def8af1a..e3a52317 100644 --- a/src/orchestrators/test.ts +++ b/src/orchestrators/test.ts @@ -1,6 +1,7 @@ import { OS, SpawnStatus } from 'codify-schemas'; import os from 'node:os'; +import fs from 'node:fs' import path from 'node:path'; import { PluginInitOrchestrator } from '../common/initialize-plugins.js'; @@ -71,9 +72,12 @@ export const TestOrchestrator = { // Add symlinks to the bind mount locations. await spawn(`tart exec ${vmName} sudo ln -s /Volumes/My\\ Shared\\ Files/codify-lib/bin/codify /usr/local/bin/codify`, { interactive: true }); await spawn(`tart exec ${vmName} ln -s /Volumes/My\\ Shared\\ Files/codify-config/${path.basename(initializationResult.project.codifyFiles[0])} /Users/admin/codify.jsonc`, { interactive: true }); - // await spawn(`sshpass -p "admin" scp -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${initializationResult.project.codifyFiles[0]} admin@${ip}:~/codify.jsonc`, { interactive: true }); + // Launch terminal and run codify apply await (args.vmOs === OS.Darwin ? spawn(`tart exec ${vmName} osascript -e "tell application \\"Terminal\\" to do script \\"cd ~ && codify apply\\""`, { interactive: true }) : spawn(`tart exec ${vmName} gnome-terminal -- bash -c "cd ~/ && codify apply"`, { interactive: true })); + + this.watchAndSyncFileChanges(initializationResult.project.codifyFiles[0], ip); + } catch (error) { ctx.log(`Error copying files to VM: ${error}`); } @@ -148,5 +152,28 @@ export const TestOrchestrator = { await sleep(1000); } + }, + + watchAndSyncFileChanges(filePath: string, ip: string): void { + const watcher = fs.watch(filePath, { persistent: false }, async (eventType) => { + if (eventType === 'change') { + ctx.log('Config file changed, syncing to VM...'); + try { + // Copy the updated config file to the VM + // This command will fail but it causes the bind mount to update for some reason. (seems like a bug in Tart). Leave this here for now. + await spawn(`sshpass -p "admin" scp -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${filePath} admin@${ip}:~/codify.jsonc`, { interactive: true }); + // ctx.log('Config file synced successfully'); + } catch (error) { + // ctx.log(`Error syncing config file: ${error}`); + } + } + }); + + // Clean up the watcher when the process finishes + const cleanupWatcher = () => { + watcher.close(); + }; + + process.once('exit', cleanupWatcher); } }; diff --git a/src/ui/components/default-component.tsx b/src/ui/components/default-component.tsx index f105a5a1..61faad09 100644 --- a/src/ui/components/default-component.tsx +++ b/src/ui/components/default-component.tsx @@ -89,16 +89,6 @@ export function DefaultComponent(props: { ) } - { - renderStatus === RenderStatus.SECRET_PROMPT && ( - - {renderData as string} - { - emitter.emit(RenderEvent.PROMPT_RESULT, password); - }}/> - - ) - } { renderStatus === RenderStatus.SUDO_PROMPT && ( diff --git a/src/ui/reporters/default-reporter.tsx b/src/ui/reporters/default-reporter.tsx index f2b4b813..70733445 100644 --- a/src/ui/reporters/default-reporter.tsx +++ b/src/ui/reporters/default-reporter.tsx @@ -189,16 +189,6 @@ export class DefaultReporter implements Reporter { return password; } - async promptSecret(prompt: string): Promise { - const password = await this.updateStateAndAwaitEvent( - () => this.updateRenderState(RenderStatus.SECRET_PROMPT, prompt), - RenderEvent.PROMPT_RESULT, - ); - - await this.displayProgress(); - return password; - } - displayPlan(plan: Plan): void { this.updateRenderState(RenderStatus.DISPLAY_PLAN, plan) } diff --git a/src/ui/reporters/json-reporter.ts b/src/ui/reporters/json-reporter.ts index 65de70a5..acb94cd8 100644 --- a/src/ui/reporters/json-reporter.ts +++ b/src/ui/reporters/json-reporter.ts @@ -47,10 +47,6 @@ export class JsonReporter implements Reporter { throw new Error(`Json reporter error: sudo required for command: ${data.command}. Make sure to preconfigure the sudo password for the Json reporter using --sudoPassword`); } - promptSecret(prompt: string): Promise { - throw new Error('Json reporter error: cannot prompt user for values while using Json reporter. Use a different reporter.'); - } - async promptUserForValues(): Promise { throw new Error('Json reporter error: cannot prompt user for values while using Json reporter. Use a different reporter.'); } diff --git a/src/ui/reporters/plain-reporter.ts b/src/ui/reporters/plain-reporter.ts index 9361230e..b7fc8dc1 100644 --- a/src/ui/reporters/plain-reporter.ts +++ b/src/ui/reporters/plain-reporter.ts @@ -106,12 +106,6 @@ export class PlainReporter implements Reporter { return result; } - async promptSecret(prompt: string): Promise { - return new Promise((resolve) => { - this.rl.question(`${prompt} (leave empty if not needed) `, (answer) => resolve(answer)); - }); - } - async displayProgress(): Promise {} async promptInput(prompt: string, error?: string): Promise { diff --git a/src/ui/reporters/reporter.ts b/src/ui/reporters/reporter.ts index 6e1bb584..f90426ea 100644 --- a/src/ui/reporters/reporter.ts +++ b/src/ui/reporters/reporter.ts @@ -64,8 +64,6 @@ export interface Reporter { promptSudo(pluginName: string, data: CommandRequestData, secureMode: boolean): Promise; - promptSecret(prompt: string): Promise; - promptUserForValues(resources: Array, promptType: PromptType): Promise; promptPressKeyToContinue(message?: string): Promise; diff --git a/src/ui/reporters/stub-reporter.ts b/src/ui/reporters/stub-reporter.ts index 8a20d298..bd4eb32f 100644 --- a/src/ui/reporters/stub-reporter.ts +++ b/src/ui/reporters/stub-reporter.ts @@ -17,7 +17,6 @@ export class StubReporter implements Reporter { async promptConfirmation(message: string): Promise { return true; } async promptOptions(message: string, options: string[]): Promise { throw new Error('Method not implemented.'); } async promptSudo(pluginName: string, data: CommandRequestData): Promise { throw new Error('Method not implemented.'); } - async promptSecret(prompt: string): Promise { throw new Error('Method not implemented.'); } async promptUserForValues(resources: Array, promptType: PromptType): Promise { throw new Error('Method not implemented.'); } async promptPressKeyToContinue(message?: string): Promise {} async displayImportResult(importResult: ImportResult): Promise {} diff --git a/src/ui/store/index.ts b/src/ui/store/index.ts index da68725f..21bfa385 100644 --- a/src/ui/store/index.ts +++ b/src/ui/store/index.ts @@ -22,7 +22,6 @@ export enum RenderStatus { PROMPT_INPUT, PROMPT_PRESS_KEY_TO_CONTINUE, SUDO_PROMPT, - SECRET_PROMPT, DISPLAY_MESSAGE, } diff --git a/src/utils/spawn.ts b/src/utils/spawn.ts index 8a938023..288154d3 100644 --- a/src/utils/spawn.ts +++ b/src/utils/spawn.ts @@ -37,9 +37,9 @@ export async function spawnSafe(cmd: string, options?: SpawnOptions, pluginName? throw new Error('Password must be specified!'); } - // if (cmd.toLowerCase().includes('sudo')) { - // throw new Error(`Command must not include sudo. Plugin (${pluginName})`) - // } + if (cmd.toLowerCase().includes('sudo')) { + throw new Error(`Command must not include sudo. Plugin (${pluginName})`) + } if (pluginName) { ctx.pluginStdout(pluginName, `Running command: ${options?.requiresRoot ? 'sudo' : ''} ${cmd}` + (options?.cwd ? `(${options?.cwd})` : '')) @@ -66,8 +66,6 @@ export async function spawnSafe(cmd: string, options?: SpawnOptions, pluginName? const initialRows = process.stdout.rows ?? 24; const command = options?.requiresRoot ? `sudo -k >/dev/null 2>&1; sudo -S <<< "${password}" -E ${ShellUtils.getDefaultShell()} ${options?.interactive ? '-i' : ''} -c "${cmd.replaceAll('"', '\\"')}"` : cmd; - console.log(command); - const args = options?.interactive ? ['-i', '-c', command] : ['-c', command] // Run the command in a pty for interactivity diff --git a/test/orchestrator/mocks/reporter.ts b/test/orchestrator/mocks/reporter.ts index 5460637a..50336ec2 100644 --- a/test/orchestrator/mocks/reporter.ts +++ b/test/orchestrator/mocks/reporter.ts @@ -89,10 +89,6 @@ export class MockReporter implements Reporter { return ''; } - async promptSecret(prompt: string): Promise { - return ''; - } - async promptUserForValues(resourceInfo: ResourceInfo[], promptType: PromptType): Promise { if (this.config?.promptUserForValues) { return this.config.promptUserForValues(resourceInfo); From 2926ba4b8be68c708f370ac8aad1e122759f677a Mon Sep 17 00:00:00 2001 From: kevinwang Date: Fri, 6 Mar 2026 13:48:09 -0500 Subject: [PATCH 4/4] feat: Added a block for non-mac os systems --- src/orchestrators/test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/orchestrators/test.ts b/src/orchestrators/test.ts index e3a52317..67d30a53 100644 --- a/src/orchestrators/test.ts +++ b/src/orchestrators/test.ts @@ -13,6 +13,7 @@ import { sleep } from '../utils/index.js'; import { spawn, spawnSafe } from '../utils/spawn.js'; import { PlanOrchestrator, PlanOrchestratorResponse } from './plan.js'; import { ValidateOrchestrator } from './validate.js'; +import { OsUtils } from '../utils/os-utils.js'; export interface TestArgs { path?: string; @@ -23,6 +24,10 @@ export interface TestArgs { export const TestOrchestrator = { async run(args: TestArgs, reporter: Reporter): Promise { + if (!OsUtils.isMacOS()) { + throw new Error('Only a MacOS host is supported currently for testing'); + } + ctx.processStarted(ProcessName.TEST); reporter.silent = true;