Skip to content
Merged
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
30 changes: 28 additions & 2 deletions src/cli/tui/hooks/__tests__/useDevDeploy.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ vi.mock('../../../operations/deploy/change-detection', () => ({
}));

function Harness({ skip }: { skip?: boolean }) {
const { steps, isComplete, error } = useDevDeploy({ skip });
const { steps, isComplete, error, managedMemoryNotice } = useDevDeploy({ skip });
return (
<Text>
steps:{steps.length} isComplete:{String(isComplete)} error:{error ?? 'null'}
steps:{steps.length} isComplete:{String(isComplete)} error:{error ?? 'null'} notice:
{managedMemoryNotice ?? 'null'}
</Text>
);
}
Expand Down Expand Up @@ -108,6 +109,31 @@ describe('useDevDeploy', () => {
});
});

it('surfaces the managed-memory heads-up from the onNotice callback', async () => {
mockHandleDeploy.mockImplementation((opts: { onNotice?: (message: string) => void }) => {
opts.onNotice?.('Managed memory: this harness automatically provisions a dedicated AgentCore Memory resource');
return Promise.resolve({ success: true });
});

const { lastFrame } = render(<Harness />);

await vi.waitFor(() => {
expect(lastFrame()).toContain('notice:Managed memory:');
expect(lastFrame()).toContain('isComplete:true');
});
});

it('leaves the managed-memory heads-up null when onNotice is not called', async () => {
mockHandleDeploy.mockResolvedValue({ success: true });

const { lastFrame } = render(<Harness />);

await vi.waitFor(() => {
expect(lastFrame()).toContain('isComplete:true');
});
expect(lastFrame()).toContain('notice:null');
});

it('populates steps from onProgress callback', async () => {
mockHandleDeploy.mockImplementation((opts: { onProgress?: (step: string, status: string) => void }) => {
opts.onProgress?.('Validate project', 'start');
Expand Down
12 changes: 10 additions & 2 deletions src/cli/tui/hooks/useDevDeploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export interface UseDevDeployResult {
isComplete: boolean;
error: string | undefined;
logPath: string | undefined;
/** Managed-memory heads-up surfaced by handleDeploy (null when not applicable) */
managedMemoryNotice: string | null;
}

export function useDevDeploy({ skip, ready = true }: UseDevDeployOptions = {}): UseDevDeployResult {
Expand All @@ -26,6 +28,7 @@ export function useDevDeploy({ skip, ready = true }: UseDevDeployOptions = {}):
const [deployDone, setDeployDone] = useState(false);
const [error, setError] = useState<string | undefined>();
const [logPath, setLogPath] = useState<string | undefined>();
const [managedMemoryNotice, setManagedMemoryNotice] = useState<string | null>(null);
const hasStarted = useRef(false);

const onProgress = useCallback((stepName: string, status: 'start' | 'success' | 'error') => {
Expand All @@ -41,6 +44,10 @@ export function useDevDeploy({ skip, ready = true }: UseDevDeployOptions = {}):
setDeployMessages(prev => [...prev, msg]);
}, []);

const onNotice = useCallback((message: string) => {
setManagedMemoryNotice(message);
}, []);

useEffect(() => {
if (skip || !ready || hasStarted.current) return;
hasStarted.current = true;
Expand Down Expand Up @@ -78,6 +85,7 @@ export function useDevDeploy({ skip, ready = true }: UseDevDeployOptions = {}):
verbose: true,
onProgress,
onDeployMessage,
onNotice,
});

if (result.logPath) {
Expand All @@ -95,10 +103,10 @@ export function useDevDeploy({ skip, ready = true }: UseDevDeployOptions = {}):
};

void run();
}, [skip, ready, onProgress, onDeployMessage]);
}, [skip, ready, onProgress, onDeployMessage, onNotice]);

// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- skip is boolean, not nullable; || is the correct operator here
const isComplete = skip || deployDone;

return { steps, deployMessages, isComplete, error, logPath };
return { steps, deployMessages, isComplete, error, logPath, managedMemoryNotice };
}
6 changes: 6 additions & 0 deletions src/cli/tui/screens/dev/DevScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ export function DevScreen(props: DevScreenProps) {
isComplete: deployComplete,
error: deployError,
logPath: deployLogPath,
managedMemoryNotice,
} = useDevDeploy({ skip: props.skipDeploy, ready: mode === 'deploying' });

const hasTransitionedFromDeployRef = useRef(false);
Expand Down Expand Up @@ -527,6 +528,11 @@ export function DevScreen(props: DevScreenProps) {
<Box marginTop={1}>
<StepProgress steps={displaySteps} />
</Box>
{managedMemoryNotice && !deployComplete && (
<Box marginTop={1}>
<Text dimColor>Note: {managedMemoryNotice}</Text>
</Box>
)}
{hasStartedCfn && (
<Box marginTop={1}>
<DeployStatus messages={deployMessages} isComplete={deployComplete} hasError={!!deployError} />
Expand Down
5 changes: 4 additions & 1 deletion src/cli/tui/screens/harness/AddHarnessScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1423,7 +1423,10 @@ export function AddHarnessScreen({
initialValue="/mnt/data/"
onSubmit={wizard.setSessionStoragePath}
onCancel={() => wizard.goBack()}
customValidation={value => (value.startsWith('/') ? true : 'Must be an absolute path')}
customValidation={value => {
const r = validateBYOMountPath(value);
return r === true ? true : r;
}}
/>
)}

Expand Down
Loading